Add JSR-107 cache annotations support

This commit adds support for the JSR-107 cache annotations alongside
the Spring's cache annotations, that is @CacheResult, @CachePut,
@CacheRemove and @CacheRemoveAll as well as related annotations
@CacheDefaults, @CacheKey and @CacheValue.

Spring's caching configuration infrastructure detects the presence of
the JSR-107 API and Spring's JCache implementation. Both
@EnableCaching and the cache namespace are able to configure the
required JCache infrastructure when necessary. Both proxy mode
and AspectJ mode are supported.

As JSR-107 permits the customization of the CacheResolver to use for
both regular and exception caches, JCacheConfigurer has been
introduced as an extension of CachingConfigurer and permits to define
those.

If an exception is cached and should be rethrown, it is cloned and
the call stack is rewritten so that it matches the calling thread each
time. If the exception cannot be cloned, the original exception is
returned.

Internally, the interceptors uses Spring's caching abstraction by default
with an adapter layer when a JSR-107 component needs to be called.
This is the case for CacheResolver and CacheKeyGenerator.

The implementation uses Spring's CacheManager abstraction behind the
scene. The standard annotations can therefore be used against any
CacheManager implementation.

Issue: SPR-9616
This commit is contained in:
Stephane Nicoll
2014-02-21 12:14:32 +01:00
parent 4cd075bb96
commit 47a4327193
91 changed files with 6408 additions and 110 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@@ -35,11 +35,12 @@ import org.springframework.util.CollectionUtils;
* Spring's annotation-driven cache management capability.
*
* @author Chris Beams
* @author Stephane Nicoll
* @since 3.1
* @see EnableCaching
*/
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {
public abstract class AbstractCachingConfiguration<C extends CachingConfigurer> implements ImportAware {
protected AnnotationAttributes enableCaching;
@@ -51,7 +52,7 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
private Collection<CacheManager> cacheManagerBeans;
@Autowired(required=false)
private Collection<CachingConfigurer> cachingConfigurers;
private Collection<C> cachingConfigurers;
@Override
@@ -82,9 +83,8 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
CachingConfigurer cachingConfigurer = cachingConfigurers.iterator().next();
this.cacheManager = cachingConfigurer.cacheManager();
this.keyGenerator = cachingConfigurer.keyGenerator();
C cachingConfigurer = cachingConfigurers.iterator().next();
useCachingConfigurer(cachingConfigurer);
}
else if (!CollectionUtils.isEmpty(cacheManagerBeans)) {
int nManagers = cacheManagerBeans.size();
@@ -95,8 +95,7 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
"to make explicit which CacheManager should be used for " +
"annotation-driven cache management.");
}
CacheManager cacheManager = cacheManagerBeans.iterator().next();
this.cacheManager = cacheManager;
this.cacheManager = cacheManagerBeans.iterator().next();
// keyGenerator remains null; will fall back to default within CacheInterceptor
}
else {
@@ -105,4 +104,13 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
"from your configuration.");
}
}
/**
* Extract the configuration from the nominated {@link CachingConfigurer}.
*/
protected void useCachingConfigurer(C config) {
this.cacheManager = config.cacheManager();
this.keyGenerator = config.keyGenerator();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@@ -16,24 +16,39 @@
package org.springframework.cache.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.util.ClassUtils;
/**
* Selects which implementation of {@link AbstractCachingConfiguration} should be used
* Select which implementation of {@link AbstractCachingConfiguration} should be used
* based on the value of {@link EnableCaching#mode} on the importing {@code @Configuration}
* class.
* <p>Detect the presence of JSR-107 and enables JCache support accordingly.
*
* @author Chris Beams
* @author Stephane Nicoll
* @since 3.1
* @see EnableCaching
* @see ProxyCachingConfiguration
* @see AnnotationConfigUtils#CACHE_ASPECT_CONFIGURATION_CLASS_NAME
* @see AnnotationConfigUtils#JCACHE_ASPECT_CONFIGURATION_CLASS_NAME
*/
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
private static final String PROXY_JCACHE_CONFIGURATION_CLASS =
"org.springframework.cache.jcache.config.ProxyJCacheConfiguration";
private static final boolean jsr107Present = ClassUtils.isPresent(
"javax.cache.Cache", CachingConfigurationSelector.class.getClassLoader());
private static final boolean jCacheImplPresent = ClassUtils.isPresent(
PROXY_JCACHE_CONFIGURATION_CLASS, CachingConfigurationSelector.class.getClassLoader());
/**
* {@inheritDoc}
* @return {@link ProxyCachingConfiguration} or {@code AspectJCacheConfiguration} for
@@ -43,12 +58,47 @@ public class CachingConfigurationSelector extends AdviceModeImportSelector<Enabl
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { AutoProxyRegistrar.class.getName(), ProxyCachingConfiguration.class.getName() };
return getProxyImports();
case ASPECTJ:
return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
return getAspectJImports();
default:
return null;
}
}
/**
* Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}.
* <p>Take care of adding the necessary JSR-107 import if it is available.
*/
private String[] getProxyImports() {
List<String> result = new ArrayList<String>();
result.add(AutoProxyRegistrar.class.getName());
result.add(ProxyCachingConfiguration.class.getName());
if (isJCacheAvailable()) {
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
}
return result.toArray(new String[result.size()]);
}
/**
* Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#ASPECTJ}.
* <p>Take care of adding the necessary JSR-107 import if it is available.
*/
private String[] getAspectJImports() {
List<String> result = new ArrayList<String>();
result.add(AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
if (isJCacheAvailable()) {
result.add(AnnotationConfigUtils.JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
}
return result.toArray(new String[result.size()]);
}
/**
* Specify if the JSR-107 API and Spring's jCache implementation are available
* in the classpath.
*/
private boolean isJCacheAvailable() {
return jsr107Present && jCacheImplPresent;
}
}

View File

@@ -75,6 +75,12 @@ import org.springframework.core.Ordered;
* proxy- or AspectJ-based advice that weaves the interceptor into the call stack when
* {@link org.springframework.cache.annotation.Cacheable @Cacheable} methods are invoked.
*
* <p>If the JSR-107 API and Spring's JCache implementation are present, the necessary
* components to manage standard cache annotations are also registered. This creates the
* proxy- or AspectJ-based advice that weaves the interceptor into the call stack when
* methods annotated with {@code CacheResult}, {@code CachePut}, {@code CacheRemove} or
* {@code CacheRemoveAll} are invoked.
*
* <p><strong>A bean of type {@link org.springframework.cache.CacheManager CacheManager}
* must be registered</strong>, as there is no reasonable default that the framework can
* use as a convention. And whereas the {@code <cache:annotation-driven>} element assumes

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@@ -35,7 +35,7 @@ import org.springframework.context.annotation.Role;
* @see CachingConfigurationSelector
*/
@Configuration
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
public class ProxyCachingConfiguration extends AbstractCachingConfiguration<CachingConfigurer> {
@Bean(name=AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
@@ -18,6 +18,8 @@ package org.springframework.cache.config;
import static org.springframework.context.annotation.AnnotationConfigUtils.*;
import org.w3c.dom.Element;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@@ -29,7 +31,7 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.w3c.dom.Element;
import org.springframework.util.ClassUtils;
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
@@ -43,11 +45,23 @@ import org.w3c.dom.Element;
* '{@code proxy-target-class}' attribute to '{@code true}', which will
* result in class-based proxies being created.
*
* <p>If the JSR-107 API and Spring's JCache implementation are present,
* the necessary infrastructure beans required to handle methods annotated
* with {@code CacheResult}, {@code CachePut}, {@code CacheRemove} or
* {@code CacheRemoveAll} are also registered.
*
* @author Costin Leau
* @author Stephane Nicoll
* @since 3.1
*/
class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser {
private static final boolean jsr107Present = ClassUtils.isPresent(
"javax.cache.Cache", AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader());
private static final boolean jCacheImplPresent = ClassUtils.isPresent(
JCACHE_OPERATION_SOURCE_CLASS, AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader());
/**
* Parses the '{@code <cache:annotation-driven>}' tag. Will
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary
@@ -62,46 +76,39 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
}
else {
// mode="proxy"
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
registerCacheAdvisor(element, parserContext);
}
return null;
}
private void registerCacheAspect(Element element, ParserContext parserContext) {
SpringCachingConfigurer.registerCacheAspect(element, parserContext);
if (jsr107Present && jCacheImplPresent) { // Register JCache aspect
JCacheCachingConfigurer.registerCacheAspect(element, parserContext);
}
}
private void registerCacheAdvisor(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
SpringCachingConfigurer.registerCacheAdvisor(element, parserContext);
if (jsr107Present && jCacheImplPresent) { // Register JCache advisor
JCacheCachingConfigurer.registerCacheAdvisor(element, parserContext);
}
}
private static void parseCacheManagerProperty(Element element, BeanDefinition def) {
def.getPropertyValues().add("cacheManager",
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
}
/**
* Registers a
* <pre class="code">
* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
* <property name="cacheManager" ref="cacheManager"/>
* <property name="keyGenerator" ref="keyGenerator"/>
* </bean>
* </pre>
*/
private void registerCacheAspect(Element element, ParserContext parserContext) {
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ASPECT_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
def.setBeanClassName(CACHE_ASPECT_CLASS_NAME);
def.setFactoryMethodName("aspectOf");
parseCacheManagerProperty(element, def);
CacheNamespaceHandler.parseKeyGenerator(element, def);
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CACHE_ASPECT_BEAN_NAME));
}
}
/**
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
* Configure the necessary infrastructure to support the Spring's caching annotations.
*/
private static class AopAutoProxyConfigurer {
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
private static class SpringCachingConfigurer {
private static void registerCacheAdvisor(Element element, ParserContext parserContext) {
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ADVISOR_BEAN_NAME)) {
Object eleSource = parserContext.extractSource(element);
@@ -139,5 +146,93 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
parserContext.registerComponent(compositeDef);
}
}
/**
* Registers a
* <pre class="code">
* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
* <property name="cacheManager" ref="cacheManager"/>
* <property name="keyGenerator" ref="keyGenerator"/>
* </bean>
* </pre>
*/
private static void registerCacheAspect(Element element, ParserContext parserContext) {
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ASPECT_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
def.setBeanClassName(CACHE_ASPECT_CLASS_NAME);
def.setFactoryMethodName("aspectOf");
parseCacheManagerProperty(element, def);
CacheNamespaceHandler.parseKeyGenerator(element, def);
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CACHE_ASPECT_BEAN_NAME));
}
}
}
/**
* Configure the necessary infrastructure to support the standard JSR-107 caching annotations.
*/
private static class JCacheCachingConfigurer {
private static void registerCacheAdvisor(Element element, ParserContext parserContext) {
if (!parserContext.getRegistry().containsBeanDefinition(JCACHE_ADVISOR_BEAN_NAME)) {
Object eleSource = parserContext.extractSource(element);
// Create the CacheOperationSource definition.
BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
// Create the CacheInterceptor definition.
RootBeanDefinition interceptorDef = new RootBeanDefinition(JCACHE_INTERCEPTOR_CLASS);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
interceptorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
// Create the CacheAdvisor definition.
RootBeanDefinition advisorDef = new RootBeanDefinition(JCACHE_ADVISOR_FACTORY_CLASS);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
parserContext.getRegistry().registerBeanDefinition(JCACHE_ADVISOR_BEAN_NAME, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, JCACHE_ADVISOR_BEAN_NAME));
parserContext.registerComponent(compositeDef);
}
}
private static void registerCacheAspect(Element element, ParserContext parserContext) {
if (!parserContext.getRegistry().containsBeanDefinition(JCACHE_ASPECT_BEAN_NAME)) {
Object eleSource = parserContext.extractSource(element);
RootBeanDefinition def = new RootBeanDefinition();
def.setBeanClassName(JCACHE_ASPECT_CLASS_NAME);
def.setFactoryMethodName("aspectOf");
BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);
String sourceName =
parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
def.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));
parserContext.registerBeanComponent(new BeanComponentDefinition(def, JCACHE_ASPECT_BEAN_NAME));
}
}
private static RootBeanDefinition createJCacheOperationSourceBeanDefinition(
Element element, Object eleSource) {
RootBeanDefinition sourceDef = new RootBeanDefinition(JCACHE_OPERATION_SOURCE_CLASS);
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
parseCacheManagerProperty(element, sourceDef);
CacheNamespaceHandler.parseKeyGenerator(element, sourceDef);
return sourceDef;
}
}
}

View File

@@ -28,7 +28,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Abstract implementation of {@link CacheOperation} that caches
@@ -68,14 +67,13 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
protected final Log logger = LogFactory.getLog(getClass());
/**
* Cache of CacheOperations, keyed by DefaultCacheKey (Method + target Class).
* Cache of CacheOperations, keyed by {@link MethodCacheKey} (Method + 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.
*/
final Map<Object, Collection<CacheOperation>> attributeCache =
new ConcurrentHashMap<Object, Collection<CacheOperation>>(1024);
/**
* Determine the caching attribute for this method invocation.
* <p>Defaults to the class's caching attribute if no method attribute is found.
@@ -123,7 +121,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
* @return the cache key (never {@code null})
*/
protected Object getCacheKey(Method method, Class<?> targetClass) {
return new DefaultCacheKey(method, targetClass);
return new MethodCacheKey(method, targetClass);
}
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
@@ -188,38 +186,4 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
protected boolean allowPublicMethodsOnly() {
return false;
}
/**
* Default cache key for the CacheOperation cache.
*/
private static class DefaultCacheKey {
private final Method method;
private final Class<?> targetClass;
public DefaultCacheKey(Method method, Class<?> targetClass) {
this.method = method;
this.targetClass = targetClass;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof DefaultCacheKey)) {
return false;
}
DefaultCacheKey otherKey = (DefaultCacheKey) other;
return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(this.targetClass,
otherKey.targetClass));
}
@Override
public int hashCode() {
return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.Set;
/**
* The base interface that all cache operations must implement.
*
* @author Stephane Nicoll
* @since 4.1
*/
public interface BasicCacheOperation {
/**
* Return the cache name(s) associated to the operation.
*/
Set<String> getCacheNames();
}

View File

@@ -182,7 +182,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
return new CacheOperationContext(operation, method, args, target, targetClass);
}
protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
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
if (this.initialized) {
@@ -204,7 +204,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
return targetClass;
}
private Object execute(Invoker invoker, CacheOperationContexts contexts) {
private Object execute(CacheOperationInvoker invoker, CacheOperationContexts contexts) {
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.NO_RESULT);
@@ -343,12 +343,6 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
}
public interface Invoker {
Object invoke();
}
private class CacheOperationContexts {
private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts =

View File

@@ -45,12 +45,13 @@ public class CacheInterceptor extends CacheAspectSupport implements MethodInterc
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Invoker aopAllianceInvoker = new Invoker() {
CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
@Override
public Object invoke() {
try {
return invocation.proceed();
} catch (Throwable ex) {
}
catch (Throwable ex) {
throw new ThrowableWrapper(ex);
}
}
@@ -58,17 +59,9 @@ public class CacheInterceptor extends CacheAspectSupport implements MethodInterc
try {
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
} catch (ThrowableWrapper th) {
throw th.original;
}
}
private static class ThrowableWrapper extends RuntimeException {
private final Throwable original;
ThrowableWrapper(Throwable original) {
this.original = original;
catch (CacheOperationInvoker.ThrowableWrapper th) {
throw th.getOriginal();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
/**
* Represent the context of the invocation of a cache operation.
*
* <p>The cache operation is static and independent of a particular invocation,
* this gathers the operation and a particular invocation.
*
* @author Stephane Nicoll
* @since 4.1
*/
public interface CacheOperationInvocationContext<O extends BasicCacheOperation> {
/**
* Return the cache operation
*/
O getOperation();
/**
* Return the target instance on which the method was invoked
*/
Object getTarget();
/**
* Return the method
*/
Method getMethod();
/**
* Return the argument used to invoke the method
*/
Object[] getArgs();
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
/**
* Abstract the invocation of a cache operation.
*
* <p>Provide a special exception that can be used to indicate that the
* underlying invocation has thrown a checked exception, allowing the
* callers to threat these in a different manner if necessary.
*
* @author Stephane Nicoll
* @since 4.1
*/
public interface CacheOperationInvoker {
/**
* Invoke the cache operation defined by this instance. Can throw a
* {@link ThrowableWrapper} if that operation wants to explicitly
* indicate that a checked exception has occurred.
* @return the result of the operation
* @throws ThrowableWrapper if a checked exception has been thrown
*/
Object invoke() throws ThrowableWrapper;
/**
* Wrap any exception thrown while invoking {@link #invoke()}
*/
@SuppressWarnings("serial")
public static class ThrowableWrapper extends RuntimeException {
private final Throwable original;
public ThrowableWrapper(Throwable original) {
super(original.getMessage(), original);
this.original = original;
}
public Throwable getOriginal() {
return original;
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.Collection;
import org.springframework.cache.Cache;
/**
* Determine the {@link Cache} instance(s) to use for an intercepted method invocation.
*
* <p>Implementations MUST be thread-safe.
*
* @author Stephane Nicoll
* @since 4.1
*/
public interface CacheResolver {
/**
* Return the cache(s) to use for the specified invocation.
*
* @param context the context of the particular invocation
* @return the cache(s) to use (never null)
*/
Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
}

View File

@@ -0,0 +1,47 @@
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Represent a method on a particular {@link Class} and is suitable as a key.
* <p>Mainly for internal use within the framework.
*
* @author Costin Leau
* @author Stephane Nicoll
* @since 4.1
*/
public final class MethodCacheKey {
private final Method method;
private final Class<?> targetClass;
public MethodCacheKey(Method method, Class<?> targetClass) {
Assert.notNull(method, "method must be set.");
Assert.notNull(targetClass, "targetClass must be set.");
this.method = method;
this.targetClass = targetClass;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof MethodCacheKey)) {
return false;
}
MethodCacheKey otherKey = (MethodCacheKey) other;
return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(this.targetClass,
otherKey.targetClass));
}
@Override
public int hashCode() {
return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.util.Assert;
/**
* A simple {@link CacheResolver} that resolves the {@link Cache} instance(s)
* based on a configurable {@link CacheManager} and the name of the
* cache(s): {@link BasicCacheOperation#getCacheNames()}
*
* @author Stephane Nicoll
* @since 4.1
* @see BasicCacheOperation#getCacheNames()
*/
public class SimpleCacheResolver implements CacheResolver {
private final CacheManager cacheManager;
public SimpleCacheResolver(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<Cache> result = new ArrayList<Cache>();
for (String cacheName : context.getOperation().getCacheNames()) {
Cache cache = cacheManager.getCache(cacheName);
Assert.notNull(cache, "Cannot find cache named '" + cacheName + "' for " + context.getOperation());
result.add(cache);
}
return result;
}
}

View File

@@ -30,11 +30,12 @@ import org.springframework.util.StringUtils;
* @see SimpleKeyGenerator
*/
@SuppressWarnings("serial")
public final class SimpleKey implements Serializable {
public class SimpleKey implements Serializable {
public static final SimpleKey EMPTY = new SimpleKey();
private final Object[] params;
private final int hashCode;
/**
@@ -45,9 +46,9 @@ public final class SimpleKey implements Serializable {
Assert.notNull(elements, "Elements must not be null");
this.params = new Object[elements.length];
System.arraycopy(elements, 0, this.params, 0, elements.length);
this.hashCode = Arrays.deepHashCode(this.params);
}
@Override
public boolean equals(Object obj) {
return (this == obj || (obj instanceof SimpleKey
@@ -55,13 +56,13 @@ public final class SimpleKey implements Serializable {
}
@Override
public int hashCode() {
return Arrays.deepHashCode(this.params);
public final int hashCode() {
return hashCode;
}
@Override
public String toString() {
return "SimpleKey [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
return getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@@ -48,6 +48,7 @@ import org.springframework.util.ClassUtils;
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @since 2.5
* @see ContextAnnotationAutowireCandidateResolver
* @see CommonAnnotationBeanPostProcessor
@@ -147,6 +148,48 @@ public class AnnotationConfigUtils {
public static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.cache.aspectj.AspectJCachingConfiguration";
/**
* The bean name of the internally managed JSR-107 cache advisor.
*/
public static final String JCACHE_ADVISOR_BEAN_NAME =
"org.springframework.cache.config.internalJCacheAdvisor";
/**
* The class name of the JSR-107 cache operation source.
*/
public static final String JCACHE_OPERATION_SOURCE_CLASS
= "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource";
/**
* The class name of the JSR-107 cache interceptor.
*/
public static final String JCACHE_INTERCEPTOR_CLASS =
"org.springframework.cache.jcache.interceptor.JCacheInterceptor";
/**
* The class name of the JSR-107 cache advisor factory.
*/
public static final String JCACHE_ADVISOR_FACTORY_CLASS =
"org.springframework.cache.jcache.interceptor.BeanFactoryJCacheOperationSourceAdvisor";
/**
* The bean name of the internally managed JSR-107 cache aspect.
*/
public static final String JCACHE_ASPECT_BEAN_NAME =
"org.springframework.cache.config.internalJCacheAspect";
/**
* The class name of the AspectJ JSR-107 cache aspect.
*/
public static final String JCACHE_ASPECT_CLASS_NAME =
"org.springframework.cache.aspectj.JCacheCacheAspect";
/**
* The name of the AspectJ JSR-107 cache aspect @{@code Configuration} class.
*/
public static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.cache.aspectj.AspectJJCacheConfiguration";
/**
* The bean name of the internally managed JPA annotation processor.
*/