Allow @Cacheable method to return java.util.Optional variant of cached value
Includes renaming of internal delegate to CacheOperationExpressionEvaluator. Issue: SPR-14230
This commit is contained in:
parent
220711d45b
commit
240f254bfc
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -27,8 +27,6 @@ import org.springframework.cache.Cache;
|
||||||
import org.springframework.context.expression.AnnotatedElementKey;
|
import org.springframework.context.expression.AnnotatedElementKey;
|
||||||
import org.springframework.context.expression.BeanFactoryResolver;
|
import org.springframework.context.expression.BeanFactoryResolver;
|
||||||
import org.springframework.context.expression.CachedExpressionEvaluator;
|
import org.springframework.context.expression.CachedExpressionEvaluator;
|
||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
|
|
||||||
|
@ -45,7 +43,7 @@ import org.springframework.expression.Expression;
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
class ExpressionEvaluator extends CachedExpressionEvaluator {
|
class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate that there is no result variable.
|
* Indicate that there is no result variable.
|
||||||
|
@ -62,8 +60,6 @@ class ExpressionEvaluator extends CachedExpressionEvaluator {
|
||||||
*/
|
*/
|
||||||
public static final String RESULT_VARIABLE = "result";
|
public static final String RESULT_VARIABLE = "result";
|
||||||
|
|
||||||
// shared param discoverer since it caches data internally
|
|
||||||
private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
|
|
||||||
|
|
||||||
private final Map<ExpressionKey, Expression> keyCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);
|
private final Map<ExpressionKey, Expression> keyCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);
|
||||||
|
|
||||||
|
@ -100,11 +96,11 @@ class ExpressionEvaluator extends CachedExpressionEvaluator {
|
||||||
Method method, Object[] args, Object target, Class<?> targetClass, Object result,
|
Method method, Object[] args, Object target, Class<?> targetClass, Object result,
|
||||||
BeanFactory beanFactory) {
|
BeanFactory beanFactory) {
|
||||||
|
|
||||||
CacheExpressionRootObject rootObject = new CacheExpressionRootObject(caches,
|
CacheExpressionRootObject rootObject = new CacheExpressionRootObject(
|
||||||
method, args, target, targetClass);
|
caches, method, args, target, targetClass);
|
||||||
Method targetMethod = getTargetMethod(targetClass, method);
|
Method targetMethod = getTargetMethod(targetClass, method);
|
||||||
CacheEvaluationContext evaluationContext = new CacheEvaluationContext(rootObject,
|
CacheEvaluationContext evaluationContext = new CacheEvaluationContext(
|
||||||
targetMethod, args, this.paramNameDiscoverer);
|
rootObject, targetMethod, args, getParameterNameDiscoverer());
|
||||||
if (result == RESULT_UNAVAILABLE) {
|
if (result == RESULT_UNAVAILABLE) {
|
||||||
evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
|
evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
|
||||||
}
|
}
|
|
@ -21,8 +21,8 @@ package org.springframework.cache.interceptor;
|
||||||
*
|
*
|
||||||
* <p>Does not provide a way to transmit checked exceptions but
|
* <p>Does not provide a way to transmit checked exceptions but
|
||||||
* provide a special exception that should be used to wrap any
|
* provide a special exception that should be used to wrap any
|
||||||
* exception that was thrown by the underlying invocation. Callers
|
* exception that was thrown by the underlying invocation.
|
||||||
* are expected to handle this issue type specifically.
|
* Callers are expected to handle this issue type specifically.
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
|
@ -38,8 +38,9 @@ public interface CacheOperationInvoker {
|
||||||
*/
|
*/
|
||||||
Object invoke() throws ThrowableWrapper;
|
Object invoke() throws ThrowableWrapper;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap any exception thrown while invoking {@link #invoke()}
|
* Wrap any exception thrown while invoking {@link #invoke()}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
class ThrowableWrapper extends RuntimeException {
|
class ThrowableWrapper extends RuntimeException {
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -27,8 +27,6 @@ import org.springframework.context.expression.AnnotatedElementKey;
|
||||||
import org.springframework.context.expression.BeanFactoryResolver;
|
import org.springframework.context.expression.BeanFactoryResolver;
|
||||||
import org.springframework.context.expression.CachedExpressionEvaluator;
|
import org.springframework.context.expression.CachedExpressionEvaluator;
|
||||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
|
|
||||||
|
@ -42,13 +40,11 @@ import org.springframework.expression.Expression;
|
||||||
*/
|
*/
|
||||||
class EventExpressionEvaluator extends CachedExpressionEvaluator {
|
class EventExpressionEvaluator extends CachedExpressionEvaluator {
|
||||||
|
|
||||||
// shared param discoverer since it caches data internally
|
|
||||||
private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
|
|
||||||
|
|
||||||
private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);
|
private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);
|
||||||
|
|
||||||
private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<AnnotatedElementKey, Method>(64);
|
private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<AnnotatedElementKey, Method>(64);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the suitable {@link EvaluationContext} for the specified event handling
|
* Create the suitable {@link EvaluationContext} for the specified event handling
|
||||||
* on the specified method.
|
* on the specified method.
|
||||||
|
@ -58,8 +54,8 @@ class EventExpressionEvaluator extends CachedExpressionEvaluator {
|
||||||
|
|
||||||
Method targetMethod = getTargetMethod(targetClass, method);
|
Method targetMethod = getTargetMethod(targetClass, method);
|
||||||
EventExpressionRootObject root = new EventExpressionRootObject(event, args);
|
EventExpressionRootObject root = new EventExpressionRootObject(event, args);
|
||||||
MethodBasedEvaluationContext evaluationContext =
|
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(
|
||||||
new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer);
|
root, targetMethod, args, getParameterNameDiscoverer());
|
||||||
if (beanFactory != null) {
|
if (beanFactory != null) {
|
||||||
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ package org.springframework.context.expression;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -35,12 +37,14 @@ public abstract class CachedExpressionEvaluator {
|
||||||
|
|
||||||
private final SpelExpressionParser parser;
|
private final SpelExpressionParser parser;
|
||||||
|
|
||||||
|
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance with the specified {@link SpelExpressionParser}.
|
* Create a new instance with the specified {@link SpelExpressionParser}.
|
||||||
*/
|
*/
|
||||||
protected CachedExpressionEvaluator(SpelExpressionParser parser) {
|
protected CachedExpressionEvaluator(SpelExpressionParser parser) {
|
||||||
Assert.notNull(parser, "Parser must not be null");
|
Assert.notNull(parser, "SpelExpressionParser must not be null");
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +63,14 @@ public abstract class CachedExpressionEvaluator {
|
||||||
return this.parser;
|
return this.parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a shared parameter name discoverer which caches data internally.
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
protected ParameterNameDiscoverer getParameterNameDiscoverer() {
|
||||||
|
return this.parameterNameDiscoverer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the {@link Expression} for the specified SpEL value
|
* Return the {@link Expression} for the specified SpEL value
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -20,12 +20,14 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import org.springframework.cache.annotation.CachePut;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.cache.annotation.Caching;
|
import org.springframework.cache.annotation.Caching;
|
||||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||||
|
@ -39,6 +41,7 @@ import org.springframework.cache.support.SimpleCacheManager;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
@ -83,11 +86,11 @@ public class CacheReproTests {
|
||||||
|
|
||||||
String key = "1";
|
String key = "1";
|
||||||
Object result = bean.getSimple("1");
|
Object result = bean.getSimple("1");
|
||||||
verify(cache, times(1)).get(key); // first call: cache miss
|
verify(cache, times(1)).get(key); // first call: cache miss
|
||||||
|
|
||||||
Object cachedResult = bean.getSimple("1");
|
Object cachedResult = bean.getSimple("1");
|
||||||
assertSame(result, cachedResult);
|
assertSame(result, cachedResult);
|
||||||
verify(cache, times(2)).get(key); // second call: cache hit
|
verify(cache, times(2)).get(key); // second call: cache hit
|
||||||
|
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
@ -100,11 +103,11 @@ public class CacheReproTests {
|
||||||
|
|
||||||
String key = "1";
|
String key = "1";
|
||||||
Object result = bean.getNeverCache("1");
|
Object result = bean.getNeverCache("1");
|
||||||
verify(cache, times(0)).get(key); // no cache hit at all, caching disabled
|
verify(cache, times(0)).get(key); // no cache hit at all, caching disabled
|
||||||
|
|
||||||
Object cachedResult = bean.getNeverCache("1");
|
Object cachedResult = bean.getNeverCache("1");
|
||||||
assertNotSame(result, cachedResult);
|
assertNotSame(result, cachedResult);
|
||||||
verify(cache, times(0)).get(key); // caching disabled
|
verify(cache, times(0)).get(key); // caching disabled
|
||||||
|
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
@ -114,8 +117,9 @@ public class CacheReproTests {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class);
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class);
|
||||||
MyCacheResolver cacheResolver = context.getBean(MyCacheResolver.class);
|
MyCacheResolver cacheResolver = context.getBean(MyCacheResolver.class);
|
||||||
Spr13081Service bean = context.getBean(Spr13081Service.class);
|
Spr13081Service bean = context.getBean(Spr13081Service.class);
|
||||||
|
|
||||||
assertNull(cacheResolver.getCache("foo").get("foo"));
|
assertNull(cacheResolver.getCache("foo").get("foo"));
|
||||||
Object result = bean.getSimple("foo"); // cache name = id
|
Object result = bean.getSimple("foo"); // cache name = id
|
||||||
assertEquals(result, cacheResolver.getCache("foo").get("foo").get());
|
assertEquals(result, cacheResolver.getCache("foo").get("foo").get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,12 +128,21 @@ public class CacheReproTests {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class);
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class);
|
||||||
Spr13081Service bean = context.getBean(Spr13081Service.class);
|
Spr13081Service bean = context.getBean(Spr13081Service.class);
|
||||||
|
|
||||||
|
|
||||||
thrown.expect(IllegalStateException.class);
|
thrown.expect(IllegalStateException.class);
|
||||||
thrown.expectMessage(MyCacheResolver.class.getName());
|
thrown.expectMessage(MyCacheResolver.class.getName());
|
||||||
bean.getSimple(null);
|
bean.getSimple(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spr14230AdaptsToOptional() {
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr14230Config.class);
|
||||||
|
Spr14230Service bean = context.getBean(Spr14230Service.class);
|
||||||
|
|
||||||
|
TestBean tb = new TestBean("tb1");
|
||||||
|
bean.insertItem(tb);
|
||||||
|
assertSame(tb, bean.findById("tb1").get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
|
@ -245,6 +258,7 @@ public class CacheReproTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
public static class Spr13081Config extends CachingConfigurerSupport {
|
public static class Spr13081Config extends CachingConfigurerSupport {
|
||||||
|
@ -259,9 +273,9 @@ public class CacheReproTests {
|
||||||
public Spr13081Service service() {
|
public Spr13081Service service() {
|
||||||
return new Spr13081Service();
|
return new Spr13081Service();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class MyCacheResolver extends AbstractCacheResolver {
|
public static class MyCacheResolver extends AbstractCacheResolver {
|
||||||
|
|
||||||
public MyCacheResolver() {
|
public MyCacheResolver() {
|
||||||
|
@ -282,6 +296,7 @@ public class CacheReproTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class Spr13081Service {
|
public static class Spr13081Service {
|
||||||
|
|
||||||
@Cacheable
|
@Cacheable
|
||||||
|
@ -290,4 +305,34 @@ public class CacheReproTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Spr14230Service {
|
||||||
|
|
||||||
|
@Cacheable("itemCache")
|
||||||
|
public Optional<TestBean> findById(String id) {
|
||||||
|
return Optional.of(new TestBean(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@CachePut(cacheNames = "itemCache", key = "#item.name")
|
||||||
|
public TestBean insertItem(TestBean item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
public static class Spr14230Config {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CacheManager cacheManager() {
|
||||||
|
return new ConcurrentMapCacheManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Spr14230Service service() {
|
||||||
|
return new Spr14230Service();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -67,35 +67,35 @@ public class CacheSyncFailureTests {
|
||||||
@Test
|
@Test
|
||||||
public void unlessSync() {
|
public void unlessSync() {
|
||||||
thrown.expect(IllegalStateException.class);
|
thrown.expect(IllegalStateException.class);
|
||||||
thrown.expectMessage("@Cacheable(sync = true) does not support unless attribute");
|
thrown.expectMessage("@Cacheable(sync=true) does not support unless attribute");
|
||||||
this.simpleService.unlessSync("key");
|
this.simpleService.unlessSync("key");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void severalCachesSync() {
|
public void severalCachesSync() {
|
||||||
thrown.expect(IllegalStateException.class);
|
thrown.expect(IllegalStateException.class);
|
||||||
thrown.expectMessage("@Cacheable(sync = true) only allows a single cache");
|
thrown.expectMessage("@Cacheable(sync=true) only allows a single cache");
|
||||||
this.simpleService.severalCachesSync("key");
|
this.simpleService.severalCachesSync("key");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void severalCachesWithResolvedSync() {
|
public void severalCachesWithResolvedSync() {
|
||||||
thrown.expect(IllegalStateException.class);
|
thrown.expect(IllegalStateException.class);
|
||||||
thrown.expectMessage("@Cacheable(sync = true) only allows a single cache");
|
thrown.expectMessage("@Cacheable(sync=true) only allows a single cache");
|
||||||
this.simpleService.severalCachesWithResolvedSync("key");
|
this.simpleService.severalCachesWithResolvedSync("key");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void syncWithAnotherOperation() {
|
public void syncWithAnotherOperation() {
|
||||||
thrown.expect(IllegalStateException.class);
|
thrown.expect(IllegalStateException.class);
|
||||||
thrown.expectMessage("@Cacheable(sync = true) cannot be combined with other cache operations");
|
thrown.expectMessage("@Cacheable(sync=true) cannot be combined with other cache operations");
|
||||||
this.simpleService.syncWithAnotherOperation("key");
|
this.simpleService.syncWithAnotherOperation("key");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void syncWithTwoGetOperations() {
|
public void syncWithTwoGetOperations() {
|
||||||
thrown.expect(IllegalStateException.class);
|
thrown.expect(IllegalStateException.class);
|
||||||
thrown.expectMessage("Only one @Cacheable(sync = true) entry is allowed");
|
thrown.expectMessage("Only one @Cacheable(sync=true) entry is allowed");
|
||||||
this.simpleService.syncWithTwoGetOperations("key");
|
this.simpleService.syncWithTwoGetOperations("key");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -47,15 +47,17 @@ import static org.junit.Assert.*;
|
||||||
*/
|
*/
|
||||||
public class ExpressionEvaluatorTests {
|
public class ExpressionEvaluatorTests {
|
||||||
|
|
||||||
private ExpressionEvaluator eval = new ExpressionEvaluator();
|
private final CacheOperationExpressionEvaluator eval = new CacheOperationExpressionEvaluator();
|
||||||
|
|
||||||
|
private final AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();
|
||||||
|
|
||||||
private AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();
|
|
||||||
|
|
||||||
private Collection<CacheOperation> getOps(String name) {
|
private Collection<CacheOperation> getOps(String name) {
|
||||||
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, name, Object.class, Object.class);
|
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, name, Object.class, Object.class);
|
||||||
return source.getCacheOperations(method, AnnotatedClass.class);
|
return this.source.getCacheOperations(method, AnnotatedClass.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleCachingSource() throws Exception {
|
public void testMultipleCachingSource() throws Exception {
|
||||||
Collection<CacheOperation> ops = getOps("multipleCaching");
|
Collection<CacheOperation> ops = getOps("multipleCaching");
|
||||||
|
@ -110,14 +112,14 @@ public class ExpressionEvaluatorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void withoutReturnValue() throws Exception {
|
public void withoutReturnValue() throws Exception {
|
||||||
EvaluationContext context = createEvaluationContext(ExpressionEvaluator.NO_RESULT);
|
EvaluationContext context = createEvaluationContext(CacheOperationExpressionEvaluator.NO_RESULT);
|
||||||
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||||
assertThat(value, nullValue());
|
assertThat(value, nullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unavailableReturnValue() throws Exception {
|
public void unavailableReturnValue() throws Exception {
|
||||||
EvaluationContext context = createEvaluationContext(ExpressionEvaluator.RESULT_UNAVAILABLE);
|
EvaluationContext context = createEvaluationContext(CacheOperationExpressionEvaluator.RESULT_UNAVAILABLE);
|
||||||
try {
|
try {
|
||||||
new SpelExpressionParser().parseExpression("#result").getValue(context);
|
new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||||
fail("Should have failed to parse expression, result not available");
|
fail("Should have failed to parse expression, result not available");
|
||||||
|
@ -134,7 +136,7 @@ public class ExpressionEvaluatorTests {
|
||||||
applicationContext.registerBeanDefinition("myBean", beanDefinition);
|
applicationContext.registerBeanDefinition("myBean", beanDefinition);
|
||||||
applicationContext.refresh();
|
applicationContext.refresh();
|
||||||
|
|
||||||
EvaluationContext context = createEvaluationContext(ExpressionEvaluator.NO_RESULT, applicationContext);
|
EvaluationContext context = createEvaluationContext(CacheOperationExpressionEvaluator.NO_RESULT, applicationContext);
|
||||||
Object value = new SpelExpressionParser().parseExpression("@myBean.class.getName()").getValue(context);
|
Object value = new SpelExpressionParser().parseExpression("@myBean.class.getName()").getValue(context);
|
||||||
assertThat(value, is(String.class.getName()));
|
assertThat(value, is(String.class.getName()));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue