diff --git a/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java b/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java index 239a7350cdc..e80cb747f39 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java +++ b/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java @@ -110,7 +110,7 @@ public class CaffeineCacheManager implements CacheManager { * Set the Caffeine to use for building each individual * {@link CaffeineCache} instance. * @see #createNativeCaffeineCache - * @see com.github.benmanes.caffeine.cache.Caffeine#build() + * @see Caffeine#build() */ public void setCaffeine(Caffeine caffeine) { Assert.notNull(caffeine, "Caffeine must not be null"); @@ -121,7 +121,7 @@ public class CaffeineCacheManager implements CacheManager { * Set the {@link CaffeineSpec} to use for building each individual * {@link CaffeineCache} instance. * @see #createNativeCaffeineCache - * @see com.github.benmanes.caffeine.cache.Caffeine#from(CaffeineSpec) + * @see Caffeine#from(CaffeineSpec) */ public void setCaffeineSpec(CaffeineSpec caffeineSpec) { doSetCaffeine(Caffeine.from(caffeineSpec)); @@ -132,7 +132,7 @@ public class CaffeineCacheManager implements CacheManager { * individual {@link CaffeineCache} instance. The given value needs to * comply with Caffeine's {@link CaffeineSpec} (see its javadoc). * @see #createNativeCaffeineCache - * @see com.github.benmanes.caffeine.cache.Caffeine#from(String) + * @see Caffeine#from(String) */ public void setCacheSpecification(String cacheSpecification) { doSetCaffeine(Caffeine.from(cacheSpecification)); @@ -149,7 +149,7 @@ public class CaffeineCacheManager implements CacheManager { * Set the Caffeine CacheLoader to use for building each individual * {@link CaffeineCache} instance, turning it into a LoadingCache. * @see #createNativeCaffeineCache - * @see com.github.benmanes.caffeine.cache.Caffeine#build(CacheLoader) + * @see Caffeine#build(CacheLoader) * @see com.github.benmanes.caffeine.cache.LoadingCache */ public void setCacheLoader(CacheLoader cacheLoader) { diff --git a/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java b/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java index fd2943c1919..f1e024c1ab4 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java @@ -38,19 +38,17 @@ public class CaffeineCacheManagerTests { @Test public void testDynamicMode() { CacheManager cm = new CaffeineCacheManager(); + Cache cache1 = cm.getCache("c1"); - boolean condition2 = cache1 instanceof CaffeineCache; - assertThat(condition2).isTrue(); + assertThat(cache1).isInstanceOf(CaffeineCache.class); Cache cache1again = cm.getCache("c1"); assertThat(cache1).isSameAs(cache1again); Cache cache2 = cm.getCache("c2"); - boolean condition1 = cache2 instanceof CaffeineCache; - assertThat(condition1).isTrue(); + assertThat(cache2).isInstanceOf(CaffeineCache.class); Cache cache2again = cm.getCache("c2"); assertThat(cache2).isSameAs(cache2again); Cache cache3 = cm.getCache("c3"); - boolean condition = cache3 instanceof CaffeineCache; - assertThat(condition).isTrue(); + assertThat(cache3).isInstanceOf(CaffeineCache.class); Cache cache3again = cm.getCache("c3"); assertThat(cache3).isSameAs(cache3again); @@ -62,19 +60,23 @@ public class CaffeineCacheManagerTests { assertThat(cache1.get("key3").get()).isNull(); cache1.evict("key3"); assertThat(cache1.get("key3")).isNull(); + assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3"); + assertThat(cache1.get("key3", () -> "value3")).isEqualTo("value3"); + cache1.evict("key3"); + assertThat(cache1.get("key3", () -> (String) null)).isNull(); + assertThat(cache1.get("key3", () -> (String) null)).isNull(); } @Test public void testStaticMode() { CaffeineCacheManager cm = new CaffeineCacheManager("c1", "c2"); + Cache cache1 = cm.getCache("c1"); - boolean condition3 = cache1 instanceof CaffeineCache; - assertThat(condition3).isTrue(); + assertThat(cache1).isInstanceOf(CaffeineCache.class); Cache cache1again = cm.getCache("c1"); assertThat(cache1).isSameAs(cache1again); Cache cache2 = cm.getCache("c2"); - boolean condition2 = cache2 instanceof CaffeineCache; - assertThat(condition2).isTrue(); + assertThat(cache2).isInstanceOf(CaffeineCache.class); Cache cache2again = cm.getCache("c2"); assertThat(cache2).isSameAs(cache2again); Cache cache3 = cm.getCache("c3"); @@ -91,12 +93,10 @@ public class CaffeineCacheManagerTests { cm.setAllowNullValues(false); Cache cache1x = cm.getCache("c1"); - boolean condition1 = cache1x instanceof CaffeineCache; - assertThat(condition1).isTrue(); + assertThat(cache1x).isInstanceOf(CaffeineCache.class); assertThat(cache1x).isNotSameAs(cache1); Cache cache2x = cm.getCache("c2"); - boolean condition = cache2x instanceof CaffeineCache; - assertThat(condition).isTrue(); + assertThat(cache2x).isInstanceOf(CaffeineCache.class); assertThat(cache2x).isNotSameAs(cache2); Cache cache3x = cm.getCache("c3"); assertThat(cache3x).isNull(); @@ -190,7 +190,7 @@ public class CaffeineCacheManagerTests { assertThat(value.get()).isEqualTo("pong"); assertThatIllegalArgumentException().isThrownBy(() -> assertThat(cache1.get("foo")).isNull()) - .withMessageContaining("I only know ping"); + .withMessageContaining("I only know ping"); } @Test diff --git a/spring-context/src/main/java/org/springframework/cache/Cache.java b/spring-context/src/main/java/org/springframework/cache/Cache.java index 8a3b904f490..648ff88e395 100644 --- a/spring-context/src/main/java/org/springframework/cache/Cache.java +++ b/spring-context/src/main/java/org/springframework/cache/Cache.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -23,14 +23,20 @@ import org.springframework.lang.Nullable; /** * Interface that defines common cache operations. * - * Note: Due to the generic use of caching, it is recommended that - * implementations allow storage of {@code null} values (for example to - * cache methods that return {@code null}). + *

Serves as an SPI for Spring's annotation-based caching model + * ({@link org.springframework.cache.annotation.Cacheable} and co) + * as well as an API for direct usage in applications. + * + *

Note: Due to the generic use of caching, it is recommended + * that implementations allow storage of {@code null} values + * (for example to cache methods that return {@code null}). * * @author Costin Leau * @author Juergen Hoeller * @author Stephane Nicoll * @since 3.1 + * @see CacheManager + * @see org.springframework.cache.annotation.Cacheable */ public interface Cache { diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java index 4710f8e1c30..d5c71acd8a0 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 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. @@ -82,12 +82,12 @@ public abstract class AbstractCacheInvoker { * Execute {@link Cache#put(Object, Object)} on the specified {@link Cache} * and invoke the error handler if an exception occurs. */ - protected void doPut(Cache cache, Object key, @Nullable Object result) { + protected void doPut(Cache cache, Object key, @Nullable Object value) { try { - cache.put(key, result); + cache.put(key, value); } catch (RuntimeException ex) { - getErrorHandler().handleCachePutError(ex, cache, key, result); + getErrorHandler().handleCachePutError(ex, cache, key, value); } } 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 11c651fc6ba..d99e31d87c2 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 @@ -397,11 +397,11 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT); - // Check if we have a cached item matching the conditions + // Check if we have a cached value matching the conditions Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); - // Collect puts from any @Cacheable miss, if no cached item is found - List cachePutRequests = new ArrayList<>(); + // Collect puts from any @Cacheable miss, if no cached value is found + List cachePutRequests = new ArrayList<>(1); if (cacheHit == null) { collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests); @@ -468,7 +468,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker private boolean hasCachePut(CacheOperationContexts contexts) { // Evaluate the conditions *without* the result object because we don't have it yet... Collection cachePutContexts = contexts.get(CachePutOperation.class); - Collection excluded = new ArrayList<>(); + Collection excluded = new ArrayList<>(1); for (CacheOperationContext context : cachePutContexts) { try { if (!context.isConditionPassing(CacheOperationExpressionEvaluator.RESULT_UNAVAILABLE)) { @@ -521,9 +521,9 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker } /** - * Find a cached item only for {@link CacheableOperation} that passes the condition. + * Find a cached value only for {@link CacheableOperation} that passes the condition. * @param contexts the cacheable operations - * @return a {@link Cache.ValueWrapper} holding the cached item, + * @return a {@link Cache.ValueWrapper} holding the cached value, * or {@code null} if none is found */ @Nullable @@ -548,9 +548,9 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker /** * Collect the {@link CachePutRequest} for all {@link CacheOperation} using - * the specified result item. + * the specified result value. * @param contexts the contexts to handle - * @param result the result item (never {@code null}) + * @param result the result value (never {@code null}) * @param putRequests the collection to update */ private void collectPutRequests(Collection contexts, @@ -721,7 +721,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker this.args = extractArgs(metadata.method, args); this.target = target; this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver); - this.cacheNames = createCacheNames(this.caches); + this.cacheNames = prepareCacheNames(this.caches); } @Override @@ -809,8 +809,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker return this.cacheNames; } - private Collection createCacheNames(Collection caches) { - Collection names = new ArrayList<>(); + private Collection prepareCacheNames(Collection caches) { + Collection names = new ArrayList<>(caches.size()); for (Cache cache : caches) { names.add(cache.getName()); } diff --git a/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java b/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java index c130f8f2698..08500e04606 100644 --- a/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java +++ b/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2023 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. @@ -24,14 +24,16 @@ import org.springframework.cache.Cache; /** * Simple cache manager working against a given collection of caches. * Useful for testing or simple caching declarations. - *

- * When using this implementation directly, i.e. not via a regular + * + *

When using this implementation directly, i.e. not via a regular * bean registration, {@link #initializeCaches()} should be invoked * to initialize its internal state once the * {@linkplain #setCaches(Collection) caches have been provided}. * * @author Costin Leau * @since 3.1 + * @see NoOpCache + * @see org.springframework.cache.concurrent.ConcurrentMapCache */ public class SimpleCacheManager extends AbstractCacheManager { diff --git a/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java b/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java index 48878ef4e18..517ba8d6f7d 100644 --- a/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java +++ b/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java @@ -118,6 +118,7 @@ class CacheReproTests { assertThat(cacheResolver.getCache("foo").get("foo")).isNull(); Object result = bean.getSimple("foo"); // cache name = id assertThat(cacheResolver.getCache("foo").get("foo").get()).isEqualTo(result); + context.close(); } @@ -127,7 +128,7 @@ class CacheReproTests { Spr13081Service bean = context.getBean(Spr13081Service.class); assertThatIllegalStateException().isThrownBy(() -> bean.getSimple(null)) - .withMessageContaining(MyCacheResolver.class.getName()); + .withMessageContaining(MyCacheResolver.class.getName()); context.close(); } @@ -146,6 +147,7 @@ class CacheReproTests { TestBean tb2 = bean.findById("tb1").get(); assertThat(tb2).isNotSameAs(tb); assertThat(cache.get("tb1").get()).isSameAs(tb2); + context.close(); } @@ -177,6 +179,7 @@ class CacheReproTests { bean.insertItem(tb); assertThat(bean.findById("tb1").get()).isSameAs(tb); assertThat(cache.get("tb1").get()).isSameAs(tb); + context.close(); } @@ -190,6 +193,7 @@ class CacheReproTests { bean.insertItem(tb); assertThat(bean.findById("tb1").get()).isSameAs(tb); assertThat(cache.get("tb1").get()).isSameAs(tb); + context.close(); }