summaryrefslogtreecommitdiff
path: root/spring-context/src/main/java/org/springframework/cache/interceptor
diff options
context:
space:
mode:
Diffstat (limited to 'spring-context/src/main/java/org/springframework/cache/interceptor')
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java43
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java216
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java61
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java194
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationExpressionEvaluator.java (renamed from spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java)30
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java5
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/CachePutOperation.java50
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/CacheableOperation.java65
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/NamedCacheResolver.java4
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java4
-rw-r--r--spring-context/src/main/java/org/springframework/cache/interceptor/VariableNotAvailableException.java4
11 files changed, 487 insertions, 189 deletions
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java
index d383b281..88d0aec5 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java
@@ -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");
* you may not use this file except in compliance with the License.
@@ -26,27 +26,24 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.core.BridgeMethodResolver;
+import org.springframework.core.MethodClassKey;
import org.springframework.util.ClassUtils;
/**
- * Abstract implementation of {@link CacheOperation} that caches
- * attributes for methods and implements a fallback policy: 1. specific
- * target method; 2. target class; 3. declaring method; 4. declaring
- * class/interface.
+ * Abstract implementation of {@link CacheOperation} that caches attributes
+ * for methods and implements a fallback policy: 1. specific target method;
+ * 2. target class; 3. declaring method; 4. declaring class/interface.
*
* <p>Defaults to using the target class's caching attribute if none is
- * associated with the target method. Any caching attribute associated
- * with the target method completely overrides a class caching attribute.
- * If none found on the target class, the interface that the invoked
- * method has been called through (in case of a JDK proxy) will be
- * checked.
+ * associated with the target method. Any caching attribute associated with
+ * the target method completely overrides a class caching attribute.
+ * If none found on the target class, the interface that the invoked method
+ * has been called through (in case of a JDK proxy) will be checked.
*
- * <p>This implementation caches attributes by method after they are
- * first used. If it is ever desirable to allow dynamic changing of
- * cacheable attributes (which is very unlikely), caching could be made
- * configurable.
+ * <p>This implementation caches attributes by method after they are first
+ * used. If it is ever desirable to allow dynamic changing of cacheable
+ * attributes (which is very unlikely), caching could be made configurable.
*
* @author Costin Leau
* @author Juergen Hoeller
@@ -69,7 +66,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
protected final Log logger = LogFactory.getLog(getClass());
/**
- * Cache of CacheOperations, keyed by {@link AnnotatedElementKey}.
+ * Cache of CacheOperations, keyed by method on a specific target class.
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
@@ -117,7 +114,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
* @return the cache key (never {@code null})
*/
protected Object getCacheKey(Method method, Class<?> targetClass) {
- return new AnnotatedElementKey(method, targetClass);
+ return new MethodClassKey(method, targetClass);
}
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
@@ -140,19 +137,23 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
// Second try is the caching operation on the target class.
opDef = findCacheOperations(specificMethod.getDeclaringClass());
- if (opDef != null) {
+ if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
if (specificMethod != method) {
- // Fall back is to look at the original method.
+ // Fallback is to look at the original method.
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
- // Last fall back is the class of the original method.
- return findCacheOperations(method.getDeclaringClass());
+ // Last fallback is the class of the original method.
+ opDef = findCacheOperations(method.getDeclaringClass());
+ if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
+ return opDef;
+ }
}
+
return null;
}
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 6dd21c1d..2de8bd8f 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
@@ -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");
* you may not use this file except in compliance with the License.
@@ -23,12 +23,16 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
@@ -36,11 +40,10 @@ import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
-import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
+import org.springframework.lang.UsesJava8;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
@@ -76,17 +79,26 @@ import org.springframework.util.StringUtils;
* @since 3.1
*/
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());
- /**
- * Cache of CacheOperationMetadata, keyed by {@link CacheOperationCacheKey}.
- */
private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache =
new ConcurrentHashMap<CacheOperationCacheKey, CacheOperationMetadata>(1024);
- private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
+ private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();
private CacheOperationSource cacheOperationSource;
@@ -94,7 +106,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
private CacheResolver cacheResolver;
- private ApplicationContext applicationContext;
+ private BeanFactory beanFactory;
private boolean initialized = false;
@@ -163,12 +175,26 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
return this.cacheResolver;
}
+ /**
+ * Set the containing {@link BeanFactory} for {@link CacheManager} and other
+ * service lookups.
+ * @since 4.3
+ */
@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) {
- this.applicationContext = applicationContext;
+ this.beanFactory = applicationContext;
}
+ @Override
public void afterPropertiesSet() {
Assert.state(getCacheOperationSource() != null, "The 'cacheOperationSources' property is required: " +
"If there are no cacheable methods, then don't use a cache aspect.");
@@ -180,7 +206,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
if (getCacheResolver() == null) {
// Lazily initialize cache resolver via default cache manager...
try {
- setCacheManager(this.applicationContext.getBean(CacheManager.class));
+ setCacheManager(this.beanFactory.getBean(CacheManager.class));
}
catch (NoUniqueBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
@@ -273,7 +299,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
* Return a bean with the specified name and type. Used to resolve services that
* are referenced by name in a {@link CacheOperation}.
* @param beanName the name of the bean, as defined by the operation
- * @param expectedType type type for the bean
+ * @param expectedType type for the bean
* @return the bean matching that name
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException if such bean does not exist
* @see CacheOperation#keyGenerator
@@ -281,7 +307,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
* @see CacheOperation#cacheResolver
*/
protected <T> T getBean(String beanName, Class<T> expectedType) {
- return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext, expectedType, beanName);
+ return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, expectedType, beanName);
}
/**
@@ -293,13 +319,12 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
}
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
- // check whether aspect is enabled
- // to cope with cases where the AJ is pulled in automatically
+ // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
if (this.initialized) {
Class<?> targetClass = getTargetClass(target);
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
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));
}
}
@@ -328,9 +353,37 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
return targetClass;
}
- private Object execute(CacheOperationInvoker invoker, CacheOperationContexts contexts) {
+ private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
+ // Special handling of synchronized invocation
+ if (contexts.isSynchronized()) {
+ CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
+ if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
+ Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
+ Cache cache = context.getCaches().iterator().next();
+ try {
+ return cache.get(key, new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ return invokeOperation(invoker);
+ }
+ });
+ }
+ catch (Cache.ValueRetrievalException ex) {
+ // The invoker wraps any Throwable in a ThrowableWrapper instance so we
+ // can just make sure that one bubbles up the stack.
+ throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
+ }
+ }
+ else {
+ // No caching required, only call the underlying method
+ return invokeOperation(invoker);
+ }
+ }
+
+
// 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
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
@@ -338,42 +391,56 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();
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 (cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
- result = cacheHit;
+ if (cacheHit != null && cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
+ // If there are no put requests, just use the cache hit
+ cacheValue = cacheHit.get();
+ if (method.getReturnType() == javaUtilOptionalClass &&
+ (cacheValue == null || cacheValue.getClass() != javaUtilOptionalClass)) {
+ returnValue = OptionalUnwrapper.wrap(cacheValue);
+ }
+ else {
+ returnValue = cacheValue;
+ }
}
-
- // Invoke the method if don't have a cache hit
- if (result == null) {
- result = new SimpleValueWrapper(invokeOperation(invoker));
+ else {
+ // Invoke the method if we don't have a cache hit
+ returnValue = invokeOperation(invoker);
+ if (returnValue != null && returnValue.getClass() == javaUtilOptionalClass) {
+ cacheValue = OptionalUnwrapper.unwrap(returnValue);
+ }
+ else {
+ cacheValue = returnValue;
+ }
}
// 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
for (CachePutRequest cachePutRequest : cachePutRequests) {
- cachePutRequest.apply(result.get());
+ cachePutRequest.apply(cacheValue);
}
// 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) {
- // 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> excluded = new ArrayList<CacheOperationContext>();
for (CacheOperationContext context : cachePutContexts) {
try {
- if (!context.isConditionPassing(ExpressionEvaluator.RESULT_UNAVAILABLE)) {
+ if (!context.isConditionPassing(CacheOperationExpressionEvaluator.RESULT_UNAVAILABLE)) {
excluded.add(context);
}
}
@@ -425,7 +492,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
* or {@code null} if none is found
*/
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
- Object result = ExpressionEvaluator.NO_RESULT;
+ Object result = CacheOperationExpressionEvaluator.NO_RESULT;
for (CacheOperationContext context : contexts) {
if (isConditionPassing(context, result)) {
Object key = generateKey(context, result);
@@ -501,18 +568,57 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts =
new LinkedMultiValueMap<Class<? extends CacheOperation>, CacheOperationContext>();
+ private final boolean sync;
+
public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
Object[] args, Object target, Class<?> targetClass) {
for (CacheOperation operation : operations) {
this.contexts.add(operation.getClass(), getOperationContext(operation, method, args, target, targetClass));
}
+ this.sync = determineSyncFlag(method);
}
public Collection<CacheOperationContext> get(Class<? extends CacheOperation> operationClass) {
Collection<CacheOperationContext> result = this.contexts.get(operationClass);
return (result != null ? result : Collections.<CacheOperationContext>emptyList());
}
+
+ public boolean isSynchronized() {
+ return this.sync;
+ }
+
+ private boolean determineSyncFlag(Method method) {
+ List<CacheOperationContext> cacheOperationContexts = this.contexts.get(CacheableOperation.class);
+ if (cacheOperationContexts == null) { // no @Cacheable operation at all
+ return false;
+ }
+ boolean syncEnabled = false;
+ for (CacheOperationContext cacheOperationContext : cacheOperationContexts) {
+ if (((CacheableOperation) cacheOperationContext.getOperation()).isSync()) {
+ syncEnabled = true;
+ break;
+ }
+ }
+ if (syncEnabled) {
+ if (this.contexts.size() > 1) {
+ throw new IllegalStateException("@Cacheable(sync=true) cannot be combined with other cache operations on '" + method + "'");
+ }
+ if (cacheOperationContexts.size() > 1) {
+ throw new IllegalStateException("Only one @Cacheable(sync=true) entry is allowed on '" + method + "'");
+ }
+ CacheOperationContext cacheOperationContext = cacheOperationContexts.iterator().next();
+ CacheableOperation operation = (CacheableOperation) cacheOperationContext.getOperation();
+ if (cacheOperationContext.getCaches().size() > 1) {
+ throw new IllegalStateException("@Cacheable(sync=true) only allows a single cache on '" + operation + "'");
+ }
+ if (StringUtils.hasText(operation.getUnless())) {
+ throw new IllegalStateException("@Cacheable(sync=true) does not support unless attribute on '" + operation + "'");
+ }
+ return true;
+ }
+ return false;
+ }
}
@@ -635,8 +741,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
}
private EvaluationContext createEvaluationContext(Object result) {
- return evaluator.createEvaluationContext(
- this.caches, this.metadata.method, this.args, this.target, this.metadata.targetClass, result);
+ return evaluator.createEvaluationContext(this.caches, this.metadata.method, this.args,
+ this.target, this.metadata.targetClass, result, beanFactory);
}
protected Collection<? extends Cache> getCaches() {
@@ -678,7 +784,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
}
- private static class CacheOperationCacheKey {
+ private static final class CacheOperationCacheKey implements Comparable<CacheOperationCacheKey> {
private final CacheOperation cacheOperation;
@@ -706,6 +812,42 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
public int hashCode() {
return (this.cacheOperation.hashCode() * 31 + this.methodCacheKey.hashCode());
}
+
+ @Override
+ public String toString() {
+ return this.cacheOperation + " on " + this.methodCacheKey;
+ }
+
+ @Override
+ public int compareTo(CacheOperationCacheKey other) {
+ int result = this.cacheOperation.getName().compareTo(other.cacheOperation.getName());
+ if (result == 0) {
+ result = this.methodCacheKey.compareTo(other.methodCacheKey);
+ }
+ return result;
+ }
+ }
+
+
+ /**
+ * 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);
+ }
}
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
index 6bb937a5..5f5e0dc0 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -20,38 +20,65 @@ package org.springframework.cache.interceptor;
* Class describing a cache 'evict' operation.
*
* @author Costin Leau
+ * @author Marcin Kamionowski
* @since 3.1
*/
public class CacheEvictOperation extends CacheOperation {
- private boolean cacheWide = false;
+ private final boolean cacheWide;
- private boolean beforeInvocation = false;
+ private final boolean beforeInvocation;
- public void setCacheWide(boolean cacheWide) {
- this.cacheWide = cacheWide;
+ /**
+ * @since 4.3
+ */
+ public CacheEvictOperation(CacheEvictOperation.Builder b) {
+ super(b);
+ this.cacheWide = b.cacheWide;
+ this.beforeInvocation = b.beforeInvocation;
}
+
public boolean isCacheWide() {
return this.cacheWide;
}
- public void setBeforeInvocation(boolean beforeInvocation) {
- this.beforeInvocation = beforeInvocation;
- }
-
public boolean isBeforeInvocation() {
return this.beforeInvocation;
}
- @Override
- protected StringBuilder getOperationDescription() {
- StringBuilder sb = super.getOperationDescription();
- sb.append(",");
- sb.append(this.cacheWide);
- sb.append(",");
- sb.append(this.beforeInvocation);
- return sb;
+
+ /**
+ * @since 4.3
+ */
+ public static class Builder extends CacheOperation.Builder {
+
+ private boolean cacheWide = false;
+
+ private boolean beforeInvocation = false;
+
+ public void setCacheWide(boolean cacheWide) {
+ this.cacheWide = cacheWide;
+ }
+
+ public void setBeforeInvocation(boolean beforeInvocation) {
+ this.beforeInvocation = beforeInvocation;
+ }
+
+ @Override
+ protected StringBuilder getOperationDescription() {
+ StringBuilder sb = super.getOperationDescription();
+ sb.append(",");
+ sb.append(this.cacheWide);
+ sb.append(",");
+ sb.append(this.beforeInvocation);
+ return sb;
+ }
+
+ public CacheEvictOperation build() {
+ return new CacheEvictOperation(this);
+ }
}
+
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
index 6264a040..271b59f1 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -27,45 +27,45 @@ import org.springframework.util.Assert;
*
* @author Costin Leau
* @author Stephane Nicoll
+ * @author Marcin Kamionowski
* @since 3.1
*/
public abstract class CacheOperation implements BasicOperation {
- private String name = "";
+ private final String name;
- private Set<String> cacheNames = Collections.emptySet();
+ private final Set<String> cacheNames;
- private String key = "";
+ private final String key;
- private String keyGenerator = "";
+ private final String keyGenerator;
- private String cacheManager = "";
+ private final String cacheManager;
- private String cacheResolver = "";
+ private final String cacheResolver;
- private String condition = "";
+ private final String condition;
+ private final String toString;
- public void setName(String name) {
- Assert.hasText(name);
- this.name = name;
- }
- public String getName() {
- return this.name;
+ /**
+ * @since 4.3
+ */
+ protected CacheOperation(Builder b) {
+ this.name = b.name;
+ this.cacheNames = b.cacheNames;
+ this.key = b.key;
+ this.keyGenerator = b.keyGenerator;
+ this.cacheManager = b.cacheManager;
+ this.cacheResolver = b.cacheResolver;
+ this.condition = b.condition;
+ this.toString = b.getOperationDescription().toString();
}
- public void setCacheName(String cacheName) {
- Assert.hasText(cacheName);
- this.cacheNames = Collections.singleton(cacheName);
- }
- public void setCacheNames(String... cacheNames) {
- this.cacheNames = new LinkedHashSet<String>(cacheNames.length);
- for (String cacheName : cacheNames) {
- Assert.hasText(cacheName, "Cache name must be non-null if specified");
- this.cacheNames.add(cacheName);
- }
+ public String getName() {
+ return this.name;
}
@Override
@@ -73,47 +73,22 @@ public abstract class CacheOperation implements BasicOperation {
return this.cacheNames;
}
- public void setKey(String key) {
- Assert.notNull(key);
- this.key = key;
- }
-
public String getKey() {
return this.key;
}
- public void setKeyGenerator(String keyGenerator) {
- Assert.notNull(keyGenerator);
- this.keyGenerator = keyGenerator;
- }
-
public String getKeyGenerator() {
return this.keyGenerator;
}
- public void setCacheManager(String cacheManager) {
- Assert.notNull(cacheManager);
- this.cacheManager = cacheManager;
- }
-
public String getCacheManager() {
return this.cacheManager;
}
- public void setCacheResolver(String cacheResolver) {
- Assert.notNull(cacheManager);
- this.cacheResolver = cacheResolver;
- }
-
public String getCacheResolver() {
return this.cacheResolver;
}
- public void setCondition(String condition) {
- Assert.notNull(condition);
- this.condition = condition;
- }
-
public String getCondition() {
return this.condition;
}
@@ -139,29 +114,116 @@ public abstract class CacheOperation implements BasicOperation {
/**
* Return an identifying description for this cache operation.
- * <p>Has to be overridden in subclasses for correct {@code equals}
- * and {@code hashCode} behavior. Alternatively, {@link #equals}
- * and {@link #hashCode} can be overridden themselves.
+ * <p>Returned value is produced by calling {@link Builder#getOperationDescription()}
+ * during object construction. This method is used in {@link #hashCode} and
+ * {@link #equals}.
+ * @see Builder#getOperationDescription()
*/
@Override
- public String toString() {
- return getOperationDescription().toString();
+ public final String toString() {
+ return this.toString;
}
+
/**
- * Return an identifying description for this caching operation.
- * <p>Available to subclasses, for inclusion in their {@code toString()} result.
+ * @since 4.3
*/
- protected StringBuilder getOperationDescription() {
- StringBuilder result = new StringBuilder(getClass().getSimpleName());
- result.append("[").append(this.name);
- result.append("] caches=").append(this.cacheNames);
- result.append(" | key='").append(this.key);
- result.append("' | keyGenerator='").append(this.keyGenerator);
- result.append("' | cacheManager='").append(this.cacheManager);
- result.append("' | cacheResolver='").append(this.cacheResolver);
- result.append("' | condition='").append(this.condition).append("'");
- return result;
+ public abstract static class Builder {
+
+ private String name = "";
+
+ private Set<String> cacheNames = Collections.emptySet();
+
+ private String key = "";
+
+ private String keyGenerator = "";
+
+ private String cacheManager = "";
+
+ private String cacheResolver = "";
+
+ private String condition = "";
+
+ public void setName(String name) {
+ Assert.hasText(name);
+ this.name = name;
+ }
+
+ public void setCacheName(String cacheName) {
+ Assert.hasText(cacheName);
+ this.cacheNames = Collections.singleton(cacheName);
+ }
+
+ public void setCacheNames(String... cacheNames) {
+ this.cacheNames = new LinkedHashSet<String>(cacheNames.length);
+ for (String cacheName : cacheNames) {
+ Assert.hasText(cacheName, "Cache name must be non-null if specified");
+ this.cacheNames.add(cacheName);
+ }
+ }
+
+ public Set<String> getCacheNames() {
+ return this.cacheNames;
+ }
+
+ public void setKey(String key) {
+ Assert.notNull(key);
+ this.key = key;
+ }
+
+ public String getKey() {
+ return this.key;
+ }
+
+ public String getKeyGenerator() {
+ return this.keyGenerator;
+ }
+
+ public String getCacheManager() {
+ return this.cacheManager;
+ }
+
+ public String getCacheResolver() {
+ return this.cacheResolver;
+ }
+
+ public void setKeyGenerator(String keyGenerator) {
+ Assert.notNull(keyGenerator);
+ this.keyGenerator = keyGenerator;
+ }
+
+ public void setCacheManager(String cacheManager) {
+ Assert.notNull(cacheManager);
+ this.cacheManager = cacheManager;
+ }
+
+ public void setCacheResolver(String cacheResolver) {
+ Assert.notNull(this.cacheManager);
+ this.cacheResolver = cacheResolver;
+ }
+
+ public void setCondition(String condition) {
+ Assert.notNull(condition);
+ this.condition = condition;
+ }
+
+ /**
+ * Return an identifying description for this caching operation.
+ * <p>Available to subclasses, for inclusion in their {@code toString()} result.
+ */
+ protected StringBuilder getOperationDescription() {
+ StringBuilder result = new StringBuilder(getClass().getSimpleName());
+ result.append("[").append(this.name);
+ result.append("] caches=").append(this.cacheNames);
+ result.append(" | key='").append(this.key);
+ result.append("' | keyGenerator='").append(this.keyGenerator);
+ result.append("' | cacheManager='").append(this.cacheManager);
+ result.append("' | cacheResolver='").append(this.cacheResolver);
+ result.append("' | condition='").append(this.condition).append("'");
+ return result;
+ }
+
+ public abstract CacheOperation build();
}
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationExpressionEvaluator.java
index 88ed1484..9111bab0 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationExpressionEvaluator.java
@@ -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");
* you may not use this file except in compliance with the License.
@@ -22,11 +22,11 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.BeanFactory;
import org.springframework.cache.Cache;
import org.springframework.context.expression.AnnotatedElementKey;
+import org.springframework.context.expression.BeanFactoryResolver;
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.Expression;
@@ -43,7 +43,7 @@ import org.springframework.expression.Expression;
* @author Stephane Nicoll
* @since 3.1
*/
-class ExpressionEvaluator extends CachedExpressionEvaluator {
+class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {
/**
* Indicate that there is no result variable.
@@ -60,8 +60,6 @@ class ExpressionEvaluator extends CachedExpressionEvaluator {
*/
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);
@@ -75,12 +73,12 @@ class ExpressionEvaluator extends CachedExpressionEvaluator {
/**
* Create an {@link EvaluationContext} without a return value.
- * @see #createEvaluationContext(Collection, Method, Object[], Object, Class, Object)
+ * @see #createEvaluationContext(Collection, Method, Object[], Object, Class, Object, BeanFactory)
*/
public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches,
- Method method, Object[] args, Object target, Class<?> targetClass) {
+ Method method, Object[] args, Object target, Class<?> targetClass, BeanFactory beanFactory) {
- return createEvaluationContext(caches, method, args, target, targetClass, NO_RESULT);
+ return createEvaluationContext(caches, method, args, target, targetClass, NO_RESULT, beanFactory);
}
/**
@@ -95,19 +93,23 @@ class ExpressionEvaluator extends CachedExpressionEvaluator {
* @return the evaluation context
*/
public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches,
- Method method, Object[] args, Object target, Class<?> targetClass, Object result) {
+ Method method, Object[] args, Object target, Class<?> targetClass, Object result,
+ BeanFactory beanFactory) {
- CacheExpressionRootObject rootObject = new CacheExpressionRootObject(caches,
- method, args, target, targetClass);
+ CacheExpressionRootObject rootObject = new CacheExpressionRootObject(
+ caches, method, args, target, targetClass);
Method targetMethod = getTargetMethod(targetClass, method);
- CacheEvaluationContext evaluationContext = new CacheEvaluationContext(rootObject,
- targetMethod, args, this.paramNameDiscoverer);
+ CacheEvaluationContext evaluationContext = new CacheEvaluationContext(
+ rootObject, targetMethod, args, getParameterNameDiscoverer());
if (result == RESULT_UNAVAILABLE) {
evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
}
else if (result != NO_RESULT) {
evaluationContext.setVariable(RESULT_VARIABLE, result);
}
+ if (beanFactory != null) {
+ evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
+ }
return evaluationContext;
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java
index 8901ba19..3529438d 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java
@@ -30,9 +30,8 @@ package org.springframework.cache.interceptor;
public interface CacheOperationInvoker {
/**
- * Invoke the cache operation defined by this instance. Wraps any
- * exception that is thrown during the invocation in a
- * {@link ThrowableWrapper}.
+ * Invoke the cache operation defined by this instance. Wraps any exception
+ * that is thrown during the invocation in a {@link ThrowableWrapper}.
* @return the result of the operation
* @throws ThrowableWrapper if an error occurred while invoking the operation
*/
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CachePutOperation.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CachePutOperation.java
index e6a61b0a..01cf15f1 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CachePutOperation.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CachePutOperation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -21,27 +21,51 @@ package org.springframework.cache.interceptor;
*
* @author Costin Leau
* @author Phillip Webb
+ * @author Marcin Kamionowski
* @since 3.1
*/
public class CachePutOperation extends CacheOperation {
- private String unless;
+ private final String unless;
- public String getUnless() {
- return unless;
+ /**
+ * @since 4.3
+ */
+ public CachePutOperation(CachePutOperation.Builder b) {
+ super(b);
+ this.unless = b.unless;
}
- public void setUnless(String unless) {
- this.unless = unless;
+
+ public String getUnless() {
+ return this.unless;
}
- @Override
- protected StringBuilder getOperationDescription() {
- StringBuilder sb = super.getOperationDescription();
- sb.append(" | unless='");
- sb.append(this.unless);
- sb.append("'");
- return sb;
+
+ /**
+ * @since 4.3
+ */
+ public static class Builder extends CacheOperation.Builder {
+
+ private String unless;
+
+ public void setUnless(String unless) {
+ this.unless = unless;
+ }
+
+ @Override
+ protected StringBuilder getOperationDescription() {
+ StringBuilder sb = super.getOperationDescription();
+ sb.append(" | unless='");
+ sb.append(this.unless);
+ sb.append("'");
+ return sb;
+ }
+
+ public CachePutOperation build() {
+ return new CachePutOperation(this);
+ }
}
+
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheableOperation.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheableOperation.java
index f9375a9a..c3414e4d 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheableOperation.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheableOperation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -21,27 +21,68 @@ package org.springframework.cache.interceptor;
*
* @author Costin Leau
* @author Phillip Webb
+ * @author Marcin Kamionowski
* @since 3.1
*/
public class CacheableOperation extends CacheOperation {
- private String unless;
+ private final String unless;
+
+ private final boolean sync;
+
+
+ /**
+ * @since 4.3
+ */
+ public CacheableOperation(CacheableOperation.Builder b) {
+ super(b);
+ this.unless = b.unless;
+ this.sync = b.sync;
+ }
public String getUnless() {
- return unless;
+ return this.unless;
}
- public void setUnless(String unless) {
- this.unless = unless;
+ public boolean isSync() {
+ return this.sync;
}
- @Override
- protected StringBuilder getOperationDescription() {
- StringBuilder sb = super.getOperationDescription();
- sb.append(" | unless='");
- sb.append(this.unless);
- sb.append("'");
- return sb;
+
+ /**
+ * @since 4.3
+ */
+ public static class Builder extends CacheOperation.Builder {
+
+ private String unless;
+
+ private boolean sync;
+
+ public void setUnless(String unless) {
+ this.unless = unless;
+ }
+
+ public void setSync(boolean sync) {
+ this.sync = sync;
+ }
+
+ @Override
+ protected StringBuilder getOperationDescription() {
+ StringBuilder sb = super.getOperationDescription();
+ sb.append(" | unless='");
+ sb.append(this.unless);
+ sb.append("'");
+ sb.append(" | sync='");
+ sb.append(this.sync);
+ sb.append("'");
+ return sb;
+ }
+
+ @Override
+ public CacheableOperation build() {
+ return new CacheableOperation(this);
+ }
}
+
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/NamedCacheResolver.java b/spring-context/src/main/java/org/springframework/cache/interceptor/NamedCacheResolver.java
index 51e95c15..f75fd2e8 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/NamedCacheResolver.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/NamedCacheResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -51,7 +51,7 @@ public class NamedCacheResolver extends AbstractCacheResolver {
@Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
- return cacheNames;
+ return this.cacheNames;
}
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java
index b9304539..e6573045 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -57,7 +57,7 @@ public class SimpleKey implements Serializable {
@Override
public final int hashCode() {
- return hashCode;
+ return this.hashCode;
}
@Override
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/VariableNotAvailableException.java b/spring-context/src/main/java/org/springframework/cache/interceptor/VariableNotAvailableException.java
index b6f52d61..6f7fdde3 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/VariableNotAvailableException.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/VariableNotAvailableException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -37,6 +37,6 @@ class VariableNotAvailableException extends EvaluationException {
public String getName() {
- return name;
+ return this.name;
}
}