|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2015 the original author or authors. |
|
|
|
* Copyright 2002-2016 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -23,6 +23,7 @@ import java.util.Collections; |
|
|
|
import java.util.LinkedList; |
|
|
|
import java.util.LinkedList; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.Optional; |
|
|
|
import java.util.concurrent.Callable; |
|
|
|
import java.util.concurrent.Callable; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
|
|
|
|
|
|
|
|
@ -30,6 +31,8 @@ import org.apache.commons.logging.Log; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
import org.apache.commons.logging.LogFactory; |
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.aop.framework.AopProxyUtils; |
|
|
|
import org.springframework.aop.framework.AopProxyUtils; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.BeanFactory; |
|
|
|
|
|
|
|
import org.springframework.beans.factory.BeanFactoryAware; |
|
|
|
import org.springframework.beans.factory.InitializingBean; |
|
|
|
import org.springframework.beans.factory.InitializingBean; |
|
|
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException; |
|
|
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException; |
|
|
|
import org.springframework.beans.factory.NoUniqueBeanDefinitionException; |
|
|
|
import org.springframework.beans.factory.NoUniqueBeanDefinitionException; |
|
|
|
@ -37,11 +40,10 @@ import org.springframework.beans.factory.SmartInitializingSingleton; |
|
|
|
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; |
|
|
|
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; |
|
|
|
import org.springframework.cache.Cache; |
|
|
|
import org.springframework.cache.Cache; |
|
|
|
import org.springframework.cache.CacheManager; |
|
|
|
import org.springframework.cache.CacheManager; |
|
|
|
import org.springframework.cache.support.SimpleValueWrapper; |
|
|
|
|
|
|
|
import org.springframework.context.ApplicationContext; |
|
|
|
import org.springframework.context.ApplicationContext; |
|
|
|
import org.springframework.context.ApplicationContextAware; |
|
|
|
|
|
|
|
import org.springframework.context.expression.AnnotatedElementKey; |
|
|
|
import org.springframework.context.expression.AnnotatedElementKey; |
|
|
|
import org.springframework.expression.EvaluationContext; |
|
|
|
import org.springframework.expression.EvaluationContext; |
|
|
|
|
|
|
|
import org.springframework.lang.UsesJava8; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
import org.springframework.util.CollectionUtils; |
|
|
|
import org.springframework.util.CollectionUtils; |
|
|
|
@ -77,17 +79,26 @@ import org.springframework.util.StringUtils; |
|
|
|
* @since 3.1 |
|
|
|
* @since 3.1 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
implements InitializingBean, SmartInitializingSingleton, ApplicationContextAware { |
|
|
|
implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static Class<?> javaUtilOptionalClass = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
javaUtilOptionalClass = |
|
|
|
|
|
|
|
ClassUtils.forName("java.util.Optional", CacheAspectSupport.class.getClassLoader()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (ClassNotFoundException ex) { |
|
|
|
|
|
|
|
// Java 8 not available - Optional references simply not supported then.
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected final Log logger = LogFactory.getLog(getClass()); |
|
|
|
protected final Log logger = LogFactory.getLog(getClass()); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Cache of CacheOperationMetadata, keyed by {@link CacheOperationCacheKey}. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache = |
|
|
|
private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache = |
|
|
|
new ConcurrentHashMap<CacheOperationCacheKey, CacheOperationMetadata>(1024); |
|
|
|
new ConcurrentHashMap<CacheOperationCacheKey, CacheOperationMetadata>(1024); |
|
|
|
|
|
|
|
|
|
|
|
private final ExpressionEvaluator evaluator = new ExpressionEvaluator(); |
|
|
|
private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator(); |
|
|
|
|
|
|
|
|
|
|
|
private CacheOperationSource cacheOperationSource; |
|
|
|
private CacheOperationSource cacheOperationSource; |
|
|
|
|
|
|
|
|
|
|
|
@ -95,7 +106,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
|
|
|
|
|
|
|
|
private CacheResolver cacheResolver; |
|
|
|
private CacheResolver cacheResolver; |
|
|
|
|
|
|
|
|
|
|
|
private ApplicationContext applicationContext; |
|
|
|
private BeanFactory beanFactory; |
|
|
|
|
|
|
|
|
|
|
|
private boolean initialized = false; |
|
|
|
private boolean initialized = false; |
|
|
|
|
|
|
|
|
|
|
|
@ -164,12 +175,26 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
return this.cacheResolver; |
|
|
|
return this.cacheResolver; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Set the containing {@link BeanFactory} for {@link CacheManager} and other |
|
|
|
|
|
|
|
* service lookups. |
|
|
|
|
|
|
|
* @since 4.3 |
|
|
|
|
|
|
|
*/ |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
|
|
|
|
public void setBeanFactory(BeanFactory beanFactory) { |
|
|
|
|
|
|
|
this.beanFactory = beanFactory; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* @deprecated as of 4.3, in favor of {@link #setBeanFactory} |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
public void setApplicationContext(ApplicationContext applicationContext) { |
|
|
|
public void setApplicationContext(ApplicationContext applicationContext) { |
|
|
|
this.applicationContext = applicationContext; |
|
|
|
this.beanFactory = applicationContext; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public void afterPropertiesSet() { |
|
|
|
public void afterPropertiesSet() { |
|
|
|
Assert.state(getCacheOperationSource() != null, "The 'cacheOperationSources' property is required: " + |
|
|
|
Assert.state(getCacheOperationSource() != null, "The 'cacheOperationSources' property is required: " + |
|
|
|
"If there are no cacheable methods, then don't use a cache aspect."); |
|
|
|
"If there are no cacheable methods, then don't use a cache aspect."); |
|
|
|
@ -181,7 +206,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
if (getCacheResolver() == null) { |
|
|
|
if (getCacheResolver() == null) { |
|
|
|
// Lazily initialize cache resolver via default cache manager...
|
|
|
|
// Lazily initialize cache resolver via default cache manager...
|
|
|
|
try { |
|
|
|
try { |
|
|
|
setCacheManager(this.applicationContext.getBean(CacheManager.class)); |
|
|
|
setCacheManager(this.beanFactory.getBean(CacheManager.class)); |
|
|
|
} |
|
|
|
} |
|
|
|
catch (NoUniqueBeanDefinitionException ex) { |
|
|
|
catch (NoUniqueBeanDefinitionException ex) { |
|
|
|
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " + |
|
|
|
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " + |
|
|
|
@ -282,7 +307,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
* @see CacheOperation#cacheResolver |
|
|
|
* @see CacheOperation#cacheResolver |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected <T> T getBean(String beanName, Class<T> expectedType) { |
|
|
|
protected <T> T getBean(String beanName, Class<T> expectedType) { |
|
|
|
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext, expectedType, beanName); |
|
|
|
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, expectedType, beanName); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -294,13 +319,12 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { |
|
|
|
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { |
|
|
|
// check whether aspect is enabled
|
|
|
|
// Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
|
|
|
|
// to cope with cases where the AJ is pulled in automatically
|
|
|
|
|
|
|
|
if (this.initialized) { |
|
|
|
if (this.initialized) { |
|
|
|
Class<?> targetClass = getTargetClass(target); |
|
|
|
Class<?> targetClass = getTargetClass(target); |
|
|
|
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass); |
|
|
|
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass); |
|
|
|
if (!CollectionUtils.isEmpty(operations)) { |
|
|
|
if (!CollectionUtils.isEmpty(operations)) { |
|
|
|
return execute(invoker, new CacheOperationContexts(operations, method, args, target, targetClass)); |
|
|
|
return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -329,12 +353,12 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
return targetClass; |
|
|
|
return targetClass; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Object execute(final CacheOperationInvoker invoker, CacheOperationContexts contexts) { |
|
|
|
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) { |
|
|
|
// Special handling of synchronized invocation
|
|
|
|
// Special handling of synchronized invocation
|
|
|
|
if (contexts.isSynchronized()) { |
|
|
|
if (contexts.isSynchronized()) { |
|
|
|
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next(); |
|
|
|
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next(); |
|
|
|
if (isConditionPassing(context, ExpressionEvaluator.NO_RESULT)) { |
|
|
|
if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) { |
|
|
|
Object key = generateKey(context, ExpressionEvaluator.NO_RESULT); |
|
|
|
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT); |
|
|
|
Cache cache = context.getCaches().iterator().next(); |
|
|
|
Cache cache = context.getCaches().iterator().next(); |
|
|
|
try { |
|
|
|
try { |
|
|
|
return cache.get(key, new Callable<Object>() { |
|
|
|
return cache.get(key, new Callable<Object>() { |
|
|
|
@ -358,7 +382,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process any early evictions
|
|
|
|
// Process any early evictions
|
|
|
|
processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.NO_RESULT); |
|
|
|
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 item matching the conditions
|
|
|
|
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); |
|
|
|
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); |
|
|
|
@ -366,42 +391,56 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
// Collect puts from any @Cacheable miss, if no cached item is found
|
|
|
|
// Collect puts from any @Cacheable miss, if no cached item is found
|
|
|
|
List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>(); |
|
|
|
List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>(); |
|
|
|
if (cacheHit == null) { |
|
|
|
if (cacheHit == null) { |
|
|
|
collectPutRequests(contexts.get(CacheableOperation.class), ExpressionEvaluator.NO_RESULT, cachePutRequests); |
|
|
|
collectPutRequests(contexts.get(CacheableOperation.class), |
|
|
|
|
|
|
|
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Cache.ValueWrapper result = null; |
|
|
|
Object cacheValue; |
|
|
|
|
|
|
|
Object returnValue; |
|
|
|
|
|
|
|
|
|
|
|
// If there are no put requests, just use the cache hit
|
|
|
|
if (cacheHit != null && cachePutRequests.isEmpty() && !hasCachePut(contexts)) { |
|
|
|
if (cachePutRequests.isEmpty() && !hasCachePut(contexts)) { |
|
|
|
// If there are no put requests, just use the cache hit
|
|
|
|
result = cacheHit; |
|
|
|
cacheValue = cacheHit.get(); |
|
|
|
|
|
|
|
if (method.getReturnType() == javaUtilOptionalClass && |
|
|
|
|
|
|
|
(cacheValue == null || cacheValue.getClass() != javaUtilOptionalClass)) { |
|
|
|
|
|
|
|
returnValue = OptionalUnwrapper.wrap(cacheValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
returnValue = cacheValue; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
// Invoke the method if don't have a cache hit
|
|
|
|
// Invoke the method if we don't have a cache hit
|
|
|
|
if (result == null) { |
|
|
|
returnValue = invokeOperation(invoker); |
|
|
|
result = new SimpleValueWrapper(invokeOperation(invoker)); |
|
|
|
if (returnValue != null && returnValue.getClass() == javaUtilOptionalClass) { |
|
|
|
|
|
|
|
cacheValue = OptionalUnwrapper.unwrap(returnValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
cacheValue = returnValue; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Collect any explicit @CachePuts
|
|
|
|
// Collect any explicit @CachePuts
|
|
|
|
collectPutRequests(contexts.get(CachePutOperation.class), result.get(), cachePutRequests); |
|
|
|
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); |
|
|
|
|
|
|
|
|
|
|
|
// Process any collected put requests, either from @CachePut or a @Cacheable miss
|
|
|
|
// Process any collected put requests, either from @CachePut or a @Cacheable miss
|
|
|
|
for (CachePutRequest cachePutRequest : cachePutRequests) { |
|
|
|
for (CachePutRequest cachePutRequest : cachePutRequests) { |
|
|
|
cachePutRequest.apply(result.get()); |
|
|
|
cachePutRequest.apply(cacheValue); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Process any late evictions
|
|
|
|
// Process any late evictions
|
|
|
|
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, result.get()); |
|
|
|
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue); |
|
|
|
|
|
|
|
|
|
|
|
return result.get(); |
|
|
|
return returnValue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean hasCachePut(CacheOperationContexts contexts) { |
|
|
|
private boolean hasCachePut(CacheOperationContexts contexts) { |
|
|
|
// Evaluate the conditions *without* the result object because we don't have it yet.
|
|
|
|
// Evaluate the conditions *without* the result object because we don't have it yet...
|
|
|
|
Collection<CacheOperationContext> cachePutContexts = contexts.get(CachePutOperation.class); |
|
|
|
Collection<CacheOperationContext> cachePutContexts = contexts.get(CachePutOperation.class); |
|
|
|
Collection<CacheOperationContext> excluded = new ArrayList<CacheOperationContext>(); |
|
|
|
Collection<CacheOperationContext> excluded = new ArrayList<CacheOperationContext>(); |
|
|
|
for (CacheOperationContext context : cachePutContexts) { |
|
|
|
for (CacheOperationContext context : cachePutContexts) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
if (!context.isConditionPassing(ExpressionEvaluator.RESULT_UNAVAILABLE)) { |
|
|
|
if (!context.isConditionPassing(CacheOperationExpressionEvaluator.RESULT_UNAVAILABLE)) { |
|
|
|
excluded.add(context); |
|
|
|
excluded.add(context); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -453,7 +492,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
* or {@code null} if none is found |
|
|
|
* or {@code null} if none is found |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) { |
|
|
|
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) { |
|
|
|
Object result = ExpressionEvaluator.NO_RESULT; |
|
|
|
Object result = CacheOperationExpressionEvaluator.NO_RESULT; |
|
|
|
for (CacheOperationContext context : contexts) { |
|
|
|
for (CacheOperationContext context : contexts) { |
|
|
|
if (isConditionPassing(context, result)) { |
|
|
|
if (isConditionPassing(context, result)) { |
|
|
|
Object key = generateKey(context, result); |
|
|
|
Object key = generateKey(context, result); |
|
|
|
@ -551,7 +590,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
|
|
|
|
|
|
|
|
private boolean determineSyncFlag(Method method) { |
|
|
|
private boolean determineSyncFlag(Method method) { |
|
|
|
List<CacheOperationContext> cacheOperationContexts = this.contexts.get(CacheableOperation.class); |
|
|
|
List<CacheOperationContext> cacheOperationContexts = this.contexts.get(CacheableOperation.class); |
|
|
|
if (cacheOperationContexts == null) { // No @Cacheable operation
|
|
|
|
if (cacheOperationContexts == null) { // no @Cacheable operation at all
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
boolean syncEnabled = false; |
|
|
|
boolean syncEnabled = false; |
|
|
|
@ -563,18 +602,18 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
} |
|
|
|
} |
|
|
|
if (syncEnabled) { |
|
|
|
if (syncEnabled) { |
|
|
|
if (this.contexts.size() > 1) { |
|
|
|
if (this.contexts.size() > 1) { |
|
|
|
throw new IllegalStateException("@Cacheable(sync = true) cannot be combined with other cache operations on '" + method + "'"); |
|
|
|
throw new IllegalStateException("@Cacheable(sync=true) cannot be combined with other cache operations on '" + method + "'"); |
|
|
|
} |
|
|
|
} |
|
|
|
if (cacheOperationContexts.size() > 1) { |
|
|
|
if (cacheOperationContexts.size() > 1) { |
|
|
|
throw new IllegalStateException("Only one @Cacheable(sync = true) entry is allowed on '" + method + "'"); |
|
|
|
throw new IllegalStateException("Only one @Cacheable(sync=true) entry is allowed on '" + method + "'"); |
|
|
|
} |
|
|
|
} |
|
|
|
CacheOperationContext cacheOperationContext = cacheOperationContexts.iterator().next(); |
|
|
|
CacheOperationContext cacheOperationContext = cacheOperationContexts.iterator().next(); |
|
|
|
CacheableOperation operation = (CacheableOperation) cacheOperationContext.getOperation(); |
|
|
|
CacheableOperation operation = (CacheableOperation) cacheOperationContext.getOperation(); |
|
|
|
if (cacheOperationContext.getCaches().size() > 1) { |
|
|
|
if (cacheOperationContext.getCaches().size() > 1) { |
|
|
|
throw new IllegalStateException("@Cacheable(sync = true) only allows a single cache on '" + operation + "'"); |
|
|
|
throw new IllegalStateException("@Cacheable(sync=true) only allows a single cache on '" + operation + "'"); |
|
|
|
} |
|
|
|
} |
|
|
|
if (StringUtils.hasText(operation.getUnless())) { |
|
|
|
if (StringUtils.hasText(operation.getUnless())) { |
|
|
|
throw new IllegalStateException("@Cacheable(sync = true) does not support unless attribute on '" + operation + "'"); |
|
|
|
throw new IllegalStateException("@Cacheable(sync=true) does not support unless attribute on '" + operation + "'"); |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -702,9 +741,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private EvaluationContext createEvaluationContext(Object result) { |
|
|
|
private EvaluationContext createEvaluationContext(Object result) { |
|
|
|
return evaluator.createEvaluationContext( |
|
|
|
return evaluator.createEvaluationContext(this.caches, this.metadata.method, this.args, |
|
|
|
this.caches, this.metadata.method, this.args, this.target, this.metadata.targetClass, |
|
|
|
this.target, this.metadata.targetClass, result, beanFactory); |
|
|
|
result, applicationContext); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected Collection<? extends Cache> getCaches() { |
|
|
|
protected Collection<? extends Cache> getCaches() { |
|
|
|
@ -790,4 +828,26 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Inner class to avoid a hard dependency on Java 8. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@UsesJava8 |
|
|
|
|
|
|
|
private static class OptionalUnwrapper { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static Object unwrap(Object optionalObject) { |
|
|
|
|
|
|
|
Optional<?> optional = (Optional<?>) optionalObject; |
|
|
|
|
|
|
|
if (!optional.isPresent()) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Object result = optional.get(); |
|
|
|
|
|
|
|
Assert.isTrue(!(result instanceof Optional), "Multi-level Optional usage not supported"); |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static Object wrap(Object value) { |
|
|
|
|
|
|
|
return Optional.ofNullable(value); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|