Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.events.CacheClearEvent;
import org.springframework.cache.interceptor.events.CacheEvictEvent;
import org.springframework.cache.interceptor.events.CacheOperationEvent;
import org.springframework.cache.interceptor.events.CachePutEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.BridgeMethodResolver;
Expand Down Expand Up @@ -139,6 +144,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker

private @Nullable SingletonSupplier<CacheResolver> cacheResolver;

private @Nullable ApplicationEventPublisher applicationEventPublisher;

private @Nullable BeanFactory beanFactory;

private boolean initialized = false;
Expand All @@ -165,6 +172,15 @@ public void configure(
() -> SimpleCacheResolver.of(SupplierUtils.resolve(cacheManager)));
}

/**
* Set the {@link ApplicationEventPublisher} to use for publishing cache operation events.
* @since 7.1
* @see CacheInterceptor#setApplicationEventPublisher(ApplicationEventPublisher)
*/
public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}


/**
* Set one or more cache operation sources which are used to find the cache
Expand Down Expand Up @@ -678,19 +694,34 @@ private void performCacheEvicts(List<CacheOperationContext> contexts, @Nullable
if (operation.isCacheWide()) {
logInvalidating(context, operation, null);
doClear(cache, operation.isBeforeInvocation());
publishEvent(new CacheClearEvent(context.metadata.method, cache));
}
else {
if (key == null) {
key = generateKey(context, result);
}
logInvalidating(context, operation, key);
doEvict(cache, key, operation.isBeforeInvocation());
publishEvent(new CacheEvictEvent(context.metadata.method, cache, key));
}
}
}
}
}

private void publishEvent(final CacheOperationEvent event) {
if (this.applicationEventPublisher != null) {
try {
this.applicationEventPublisher.publishEvent(event);
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to publish " + event, ex);
}
}
}
}

private void logInvalidating(CacheOperationContext context, CacheEvictOperation operation, @Nullable Object key) {
if (logger.isTraceEnabled()) {
logger.trace("Invalidating " + (key != null ? "cache key [" + key + "]" : "entire cache") +
Expand Down Expand Up @@ -1049,6 +1080,7 @@ public void performCachePut(@Nullable Object value) {
}
for (Cache cache : this.context.getCaches()) {
doPut(cache, key, value);
publishEvent(new CachePutEvent(this.context.metadata.method, cache, key, value));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.aopalliance.intercept.MethodInvocation;
import org.jspecify.annotations.Nullable;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.util.Assert;

/**
Expand All @@ -43,7 +45,11 @@
* @since 3.1
*/
@SuppressWarnings("serial")
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, ApplicationEventPublisherAware, Serializable {
@Override
public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
super.setApplicationEventPublisher(applicationEventPublisher);
}

@Override
public @Nullable Object invoke(final MethodInvocation invocation) throws Throwable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cache.interceptor.events;

import java.lang.reflect.Method;

import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.CacheAspectSupport;

/**
* Event published after a cache has been entirely cleared.
*
* @author Anıl Şenocak
* @since 7.1
* @see CacheOperationEvent
* @see CacheAspectSupport
*/
@SuppressWarnings("serial")
public class CacheClearEvent extends CacheOperationEvent {

/**
* Create a new {@code CacheClearEvent}.
*
* @param method the method that triggered the cache operation
* @param cache the cache that was cleared
*/
public CacheClearEvent(Method method, Cache cache) {
super(method, cache, null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cache.interceptor.events;

import java.lang.reflect.Method;

import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.CacheAspectSupport;

/**
* Event published after a single cache entry has been evicted.
*
* @author Anıl Şenocak
* @since 7.1
* @see CacheOperationEvent
* @see CacheAspectSupport
*/
@SuppressWarnings("serial")
public class CacheEvictEvent extends CacheOperationEvent {

/**
* Create a new {@code CacheEvictEvent}.
*
* @param method the method that triggered the cache operation
* @param cache the cache from which the entry was evicted
* @param key the cache key that was evicted
*/
public CacheEvictEvent(Method method, Cache cache, Object key) {
super(method, cache, key);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cache.interceptor.events;

import java.lang.reflect.Method;

import org.jspecify.annotations.Nullable;

import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.CacheAspectSupport;
import org.springframework.context.ApplicationEvent;
import org.springframework.util.ClassUtils;

/**
* Base class for cache operation events that are published when a cache entry is created, updated, or evicted.
*
* @author Anıl Şenocak
* @since 7.1
* @see CachePutEvent
* @see CacheEvictEvent
* @see CacheClearEvent
* @see CacheAspectSupport
*/
@SuppressWarnings("serial")
public abstract class CacheOperationEvent extends ApplicationEvent {

private final Method method;

private final Cache cache;

private final @Nullable Object key;

/**
* Create a new {@code CacheOperationEvent}.
*
* @param method the method that triggered the cache operation
* @param cache the cache on which the operation was performed
* @param key the cache key, or {@code null} for cache-wide clear operations
*/
protected CacheOperationEvent(Method method, Cache cache, @Nullable Object key) {
super(method);
this.method = method;
this.cache = cache;
this.key = key;
}

/**
* Return the method that triggered the cache operation.
*/
public Method getMethod() {
return this.method;
}

/**
* Return the cache on which the operation was performed.
*/
public Cache getCache() {
return this.cache;
}

/**
* Return the cache key that was affected, or {@code null}
* for cache-wide clear operations.
*/
public @Nullable Object getKey() {
return this.key;
}

/**
* Return the cache name the operation was performed on.
*/
public String getCacheName() {
return this.cache.getName();
}


@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append(": ").append(ClassUtils.getQualifiedMethodName(this.method));
sb.append(" on cache '").append(getCacheName()).append("'");
if (this.key != null) {
sb.append(" [").append(this.key).append("]");
}
return sb.toString();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cache.interceptor.events;

import java.lang.reflect.Method;

import org.jspecify.annotations.Nullable;

import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.CacheAspectSupport;

/**
* Event published after a cache entry has been created or updated.
*
* @author Anıl Şenocak
* @since 7.1
* @see CacheOperationEvent
* @see CacheAspectSupport
*/
@SuppressWarnings("serial")
public class CachePutEvent extends CacheOperationEvent {
private final @Nullable Object value;


/**
* Create a new {@code CachePutEvent}.
*
* @param method the method that triggered the cache operation
* @param cache the cache on which the entry was put
* @param key the cache key
* @param value the value that was cached
*/
public CachePutEvent(Method method, Cache cache, Object key, @Nullable Object value) {
super(method, cache, key);
this.value = value;
}


/**
* Return the value that was placed into the cache.
*/
public @Nullable Object getValue() {
return this.value;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* AOP-based solution for declarative caching demarcation.
* Builds on the AOP infrastructure in org.springframework.aop.framework.
* Any POJO can be cache-advised with Spring.
*/
@NullMarked
package org.springframework.cache.interceptor.events;

import org.jspecify.annotations.NullMarked;
Loading