From d798e58228572eb9a4949721db664a52098c859f Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 2 Jul 2026 14:17:45 +0300 Subject: [PATCH 1/6] Publish ApplicationEvents for cache operations Signed-off-by: TURKCELL\TCASENOCAK --- .../cache/interceptor/CacheAspectSupport.java | 32 +++ .../cache/interceptor/CacheInterceptor.java | 8 +- .../interceptor/events/CacheClearEvent.java | 29 ++ .../interceptor/events/CacheEvictEvent.java | 30 +++ .../events/CacheOperationEvent.java | 85 ++++++ .../interceptor/events/CachePutEvent.java | 43 +++ .../interceptor/events/package-info.java | 9 + .../interceptor/CacheOperationEventTests.java | 247 ++++++++++++++++++ 8 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java create mode 100644 spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java create mode 100644 spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java create mode 100644 spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java create mode 100644 spring-context/src/main/java/org/springframework/cache/interceptor/events/package-info.java create mode 100644 spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java index 2b2bd0110ad7..71b50f262f5c 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java @@ -35,6 +35,11 @@ import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +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 reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -139,6 +144,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker private @Nullable SingletonSupplier cacheResolver; + private @Nullable ApplicationEventPublisher applicationEventPublisher; + private @Nullable BeanFactory beanFactory; private boolean initialized = false; @@ -165,6 +172,16 @@ public void configure( () -> SimpleCacheResolver.of(SupplierUtils.resolve(cacheManager))); } + /** + * Set the {@link ApplicationEventPublisher} to use for publishing cache operation events. + * + * @see CacheInterceptor#setApplicationEventPublisher(ApplicationEventPublisher) + * @since 7.1 + */ + public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + /** * Set one or more cache operation sources which are used to find the cache @@ -678,6 +695,7 @@ private void performCacheEvicts(List contexts, @Nullable if (operation.isCacheWide()) { logInvalidating(context, operation, null); doClear(cache, operation.isBeforeInvocation()); + publishEvent(new CacheClearEvent(context.metadata.method, cache)); } else { if (key == null) { @@ -685,12 +703,25 @@ private void performCacheEvicts(List contexts, @Nullable } 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") + @@ -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)); } } } diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java index c67b309132a4..66e0be7a3995 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java @@ -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; /** @@ -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 { diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java new file mode 100644 index 000000000000..59af5467f386 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java @@ -0,0 +1,29 @@ +package org.springframework.cache.interceptor.events; + +import org.springframework.cache.Cache; +import org.springframework.cache.interceptor.CacheAspectSupport; + +import java.lang.reflect.Method; + +/** + * Event published after a cache has been entirely cleared. + * + * @author Anıl Şenocak + * @see CacheOperationEvent + * @see CacheAspectSupport + * @since 7.1 + */ +@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); + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java new file mode 100644 index 000000000000..bddefbf014f1 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java @@ -0,0 +1,30 @@ +package org.springframework.cache.interceptor.events; + +import org.springframework.cache.Cache; +import org.springframework.cache.interceptor.CacheAspectSupport; + +import java.lang.reflect.Method; + +/** + * Event published after a single cache entry has been evicted. + * + * @author Anıl Şenocak + * @see CacheOperationEvent + * @see CacheAspectSupport + * @since 7.1 + */ +@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); + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java new file mode 100644 index 000000000000..175d9b5f0185 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java @@ -0,0 +1,85 @@ +package org.springframework.cache.interceptor.events; + +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; + +import java.lang.reflect.Method; + +/** + * Base class for cache operation events that are published when a cache entry is created, updated, or evicted. + * + * @author Anıl Şenocak + * @see CachePutEvent + * @see CacheEvictEvent + * @see CacheClearEvent + * @see CacheAspectSupport + * @since 7.1 + */ +@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(); + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java new file mode 100644 index 000000000000..46aa794e42c1 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java @@ -0,0 +1,43 @@ +package org.springframework.cache.interceptor.events; + +import org.jspecify.annotations.Nullable; +import org.springframework.cache.Cache; +import org.springframework.cache.interceptor.CacheAspectSupport; + +import java.lang.reflect.Method; + +/** + * Event published after a cache entry has been created or updated. + * + * @author Anıl Şenocak + * @see CacheOperationEvent + * @see CacheAspectSupport + * @since 7.1 + */ +@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; + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/package-info.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/package-info.java new file mode 100644 index 000000000000..7d3dd24cea1e --- /dev/null +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/package-info.java @@ -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; diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java new file mode 100644 index 000000000000..525b890e294d --- /dev/null +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java @@ -0,0 +1,247 @@ +package org.springframework.cache.interceptor; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.EnableCaching; +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.cache.support.SimpleCacheManager; +import org.springframework.cache.support.SimpleValueWrapper; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for cache operation events published by the caching infrastructure. + * + * @author Anıl Şenocak + * @since 7.1 + */ +class CacheOperationEventTests { + + private AnnotationConfigApplicationContext context; + + private Cache cache; + + private SimpleService simpleService; + + private CacheEventListener listener; + + private Method getMethod; + + private Method putMethod; + + private Method evictMethod; + + private Method clearMethod; + + private Method getFutureMethod; + + @BeforeEach + void setup() throws Exception { + this.context = new AnnotationConfigApplicationContext(Config.class); + this.cache = context.getBean("mockCache", Cache.class); + this.simpleService = context.getBean(SimpleService.class); + this.listener = context.getBean(CacheEventListener.class); + this.getMethod = SimpleService.class.getMethod("get", long.class); + this.putMethod = SimpleService.class.getMethod("put", long.class); + this.evictMethod = SimpleService.class.getMethod("evict", long.class); + this.clearMethod = SimpleService.class.getMethod("clear"); + this.getFutureMethod = SimpleService.class.getMethod("getFuture", long.class); + } + + @Test + void cacheableHit_noEvent() { + given(this.cache.get(0L)).willReturn(new SimpleValueWrapper(42L)); + + this.simpleService.get(0L); + + assertThat(this.listener.events).isEmpty(); + } + + @Test + void cacheableMiss_publishesPutEvent() { + given(this.cache.get(0L)).willReturn(null); + + this.simpleService.get(0L); + + assertThat(this.listener.events).hasSize(1); + assertThat(this.listener.events.get(0)) + .isExactlyInstanceOf(CachePutEvent.class) + .satisfies(event -> assertThat(event.getMethod()).isEqualTo(this.getMethod)) + .satisfies(event -> assertThat(event.getCacheName()).isEqualTo("test")) + .satisfies(event -> assertThat(event.getKey()).isEqualTo(0L)); + assertThat(this.listener.events.get(0)) + .satisfies(event -> assertThat(((CachePutEvent) event).getValue()).isEqualTo(0L)); + } + + @Test + void cachePut_publishesPutEvent() { + given(this.cache.get(0L)).willReturn(null); + + this.simpleService.put(0L); + + assertThat(this.listener.events).hasSize(1); + assertThat(this.listener.events.get(0)) + .isExactlyInstanceOf(CachePutEvent.class) + .satisfies(event -> assertThat(event.getMethod()).isEqualTo(this.putMethod)) + .satisfies(event -> assertThat(event.getCacheName()).isEqualTo("test")) + .satisfies(event -> assertThat(event.getKey()).isEqualTo(0L)); + assertThat(this.listener.events.get(0)) + .satisfies(event -> assertThat(((CachePutEvent) event).getValue()).isEqualTo(0L)); + } + + @Test + void cacheEvict_publishesEvictEvent() { + this.simpleService.evict(0L); + + assertThat(this.listener.events).hasSize(1); + assertThat(this.listener.events.get(0)) + .isExactlyInstanceOf(CacheEvictEvent.class) + .satisfies(event -> assertThat(event.getMethod()).isEqualTo(this.evictMethod)) + .satisfies(event -> assertThat(event.getCacheName()).isEqualTo("test")) + .satisfies(event -> assertThat(event.getKey()).isEqualTo(0L)); + } + + @Test + void cacheEvictAll_publishesClearEvent() { + this.simpleService.clear(); + + assertThat(this.listener.events).hasSize(1); + assertThat(this.listener.events.get(0)) + .isExactlyInstanceOf(CacheClearEvent.class) + .satisfies(event -> assertThat(event.getMethod()).isEqualTo(this.clearMethod)) + .satisfies(event -> assertThat(event.getCacheName()).isEqualTo("test")) + .satisfies(event -> assertThat(event.getKey()).isNull()); + } + + @Test + void cacheableMissWithMultipleCaches_publishesMultiplePutEvents() { + this.simpleService.getMultiple(0L); + + assertThat(this.listener.events).hasSize(2); + assertThat(this.listener.events.get(0)) + .isExactlyInstanceOf(CachePutEvent.class) + .satisfies(event -> assertThat(event.getCacheName()).isEqualTo("primary")); + assertThat(this.listener.events.get(1)) + .isExactlyInstanceOf(CachePutEvent.class) + .satisfies(event -> assertThat(event.getCacheName()).isEqualTo("secondary")); + } + + @Test + void cacheableFuture_publishesEventOnCompletion() throws Exception { + given(this.cache.retrieve(eq(0L))).willReturn(null); + + this.simpleService.getFuture(0L).get(); + + assertThat(this.listener.events).hasSize(1); + assertThat(this.listener.events.get(0)) + .isExactlyInstanceOf(CachePutEvent.class) + .satisfies(event -> assertThat(event.getMethod()).isEqualTo(this.getFutureMethod)) + .satisfies(event -> assertThat(event.getCacheName()).isEqualTo("test")) + .satisfies(event -> assertThat(event.getKey()).isEqualTo(0L)); + } + + @Configuration + @EnableCaching + static class Config { + + @Bean + public SimpleService simpleService() { + return new SimpleService(); + } + + @Bean + public CacheEventListener cacheEventListener() { + return new CacheEventListener(); + } + + @Bean + public CacheManager cacheManager(Cache mockCache) { + SimpleCacheManager cacheManager = new SimpleCacheManager(); + Cache primary = createMockCache(); + given(primary.getName()).willReturn("primary"); + Cache secondary = createMockCache(); + given(secondary.getName()).willReturn("secondary"); + cacheManager.setCaches(List.of(mockCache, primary, secondary)); + return cacheManager; + } + + @Bean + public Cache mockCache() { + Cache cache = createMockCache(); + given(cache.getName()).willReturn("test"); + return cache; + } + + private static Cache createMockCache() { + return mock(Cache.class); + } + + } + + @CacheConfig(cacheNames = "test") + public static class SimpleService { + + private AtomicLong counter = new AtomicLong(); + + @Cacheable + public Object get(long id) { + return this.counter.getAndIncrement(); + } + + @CachePut + public Object put(long id) { + return this.counter.getAndIncrement(); + } + + @CacheEvict + public void evict(long id) { + } + + @CacheEvict(allEntries = true) + public void clear() { + } + + @Cacheable(cacheNames = {"primary", "secondary"}) + public Object getMultiple(long id) { + return this.counter.getAndIncrement(); + } + + @Cacheable + public CompletableFuture getFuture(long id) { + return CompletableFuture.completedFuture(this.counter.getAndIncrement()); + } + } + + public static class CacheEventListener implements ApplicationListener { + + public final List events = new ArrayList<>(); + + @Override + public void onApplicationEvent(CacheOperationEvent event) { + this.events.add(event); + } + + } +} From e7c36d02c975ebd5db55838300828c6e0ecd11a4 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 2 Jul 2026 14:49:03 +0300 Subject: [PATCH 2/6] Publish ApplicationEvents for cache operations Signed-off-by: TURKCELL\TCASENOCAK --- .../cache/interceptor/CacheAspectSupport.java | 16 ++++++------- .../interceptor/events/CacheClearEvent.java | 22 +++++++++++++++--- .../interceptor/events/CacheEvictEvent.java | 22 +++++++++++++++--- .../events/CacheOperationEvent.java | 23 ++++++++++++++++--- .../interceptor/events/CachePutEvent.java | 23 ++++++++++++++++--- 5 files changed, 86 insertions(+), 20 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java index 71b50f262f5c..c67fd1f2ca42 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java @@ -35,11 +35,6 @@ import org.jspecify.annotations.Nullable; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; -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 reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -54,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; @@ -174,9 +174,8 @@ public void configure( /** * Set the {@link ApplicationEventPublisher} to use for publishing cache operation events. - * - * @see CacheInterceptor#setApplicationEventPublisher(ApplicationEventPublisher) * @since 7.1 + * @see CacheInterceptor#setApplicationEventPublisher(ApplicationEventPublisher) */ public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; @@ -714,7 +713,8 @@ private void publishEvent(final CacheOperationEvent event) { if (this.applicationEventPublisher != null) { try { this.applicationEventPublisher.publishEvent(event); - } catch (Throwable ex) { + } + catch (Throwable ex) { if (logger.isWarnEnabled()) { logger.warn("Failed to publish " + event, ex); } diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java index 59af5467f386..4aed2254f482 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheClearEvent.java @@ -1,17 +1,33 @@ +/* + * 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; -import java.lang.reflect.Method; - /** * Event published after a cache has been entirely cleared. * * @author Anıl Şenocak + * @since 7.1 * @see CacheOperationEvent * @see CacheAspectSupport - * @since 7.1 */ @SuppressWarnings("serial") public class CacheClearEvent extends CacheOperationEvent { diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java index bddefbf014f1..14f74fa69695 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheEvictEvent.java @@ -1,17 +1,33 @@ +/* + * 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; -import java.lang.reflect.Method; - /** * Event published after a single cache entry has been evicted. * * @author Anıl Şenocak + * @since 7.1 * @see CacheOperationEvent * @see CacheAspectSupport - * @since 7.1 */ @SuppressWarnings("serial") public class CacheEvictEvent extends CacheOperationEvent { diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java index 175d9b5f0185..8d1ec61c3549 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CacheOperationEvent.java @@ -1,22 +1,39 @@ +/* + * 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; -import java.lang.reflect.Method; - /** * 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 - * @since 7.1 */ @SuppressWarnings("serial") public abstract class CacheOperationEvent extends ApplicationEvent { diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java index 46aa794e42c1..cb3114edad96 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/events/CachePutEvent.java @@ -1,18 +1,35 @@ +/* + * 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 java.lang.reflect.Method; - /** * Event published after a cache entry has been created or updated. * * @author Anıl Şenocak + * @since 7.1 * @see CacheOperationEvent * @see CacheAspectSupport - * @since 7.1 */ @SuppressWarnings("serial") public class CachePutEvent extends CacheOperationEvent { From 4031babef83a7c708f1572641e5e282848006e60 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 2 Jul 2026 14:59:13 +0300 Subject: [PATCH 3/6] Publish ApplicationEvents for cache operations Signed-off-by: TURKCELL\TCASENOCAK --- .../interceptor/CacheOperationEventTests.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java index 525b890e294d..be462ddfee80 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java @@ -1,7 +1,24 @@ +/* + * 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; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheConfig; From becc1292d43ff853a66ae7f66da97061e0c2d5ba Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 2 Jul 2026 15:04:39 +0300 Subject: [PATCH 4/6] Publish ApplicationEvents for cache operations Signed-off-by: TURKCELL\TCASENOCAK --- .../cache/interceptor/CacheOperationEventTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java index be462ddfee80..8321c5ae4044 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java @@ -16,6 +16,8 @@ package org.springframework.cache.interceptor; +import java.lang.reflect.Method; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -37,7 +39,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; From f08659a5c67bd7d3bc7f3558c20a58b229b7d861 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 2 Jul 2026 15:13:15 +0300 Subject: [PATCH 5/6] Publish ApplicationEvents for cache operations Signed-off-by: TURKCELL\TCASENOCAK --- .../cache/interceptor/CacheOperationEventTests.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java index 8321c5ae4044..2c11e7e4cd59 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java @@ -18,6 +18,11 @@ import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,11 +44,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicLong; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; From 14d3b502c143f5cf5cd84ea95cc3ec7ac641ffd3 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 2 Jul 2026 15:23:27 +0300 Subject: [PATCH 6/6] Publish ApplicationEvents for cache operations Signed-off-by: TURKCELL\TCASENOCAK --- .../cache/interceptor/CacheOperationEventTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java index 2c11e7e4cd59..0518eea042f4 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/CacheOperationEventTests.java @@ -17,7 +17,6 @@ package org.springframework.cache.interceptor; import java.lang.reflect.Method; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture;