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-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.
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.interceptor.CacheAspectSupport;
import org.springframework.cache.interceptor.CacheOperationInvoker;
import org.springframework.cache.interceptor.CacheOperationSource;
/**
@@ -56,7 +57,7 @@ public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method method = methodSignature.getMethod();
Invoker aspectJInvoker = new Invoker() {
CacheOperationInvoker aspectJInvoker = new CacheOperationInvoker() {
public Object invoke() {
return proceed(cachedObject);
}

View File

@@ -47,4 +47,5 @@ public class AspectJCachingConfiguration extends AbstractCachingConfiguration {
}
return cacheAspect;
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.aspectj;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.annotation.AbstractCachingConfiguration;
import org.springframework.cache.jcache.config.AbstractJCacheConfiguration;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
/**
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
* to enable AspectJ-based annotation-driven cache management for standard JSR-107
* annotations.
*
* @author Stephane Nicoll
* @since 4.1
* @see org.springframework.cache.annotation.EnableCaching
* @see org.springframework.cache.annotation.CachingConfigurationSelector
*/
@Configuration
public class AspectJJCacheConfiguration extends AbstractJCacheConfiguration {
@Bean(name=AnnotationConfigUtils.JCACHE_ASPECT_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public JCacheCacheAspect cacheAspect() {
JCacheCacheAspect cacheAspect = JCacheCacheAspect.aspectOf();
cacheAspect.setCacheOperationSource(cacheOperationSource());
return cacheAspect;
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.aspectj;
import java.lang.reflect.Method;
import javax.cache.annotation.CachePut;
import javax.cache.annotation.CacheRemove;
import javax.cache.annotation.CacheRemoveAll;
import javax.cache.annotation.CacheResult;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.interceptor.CacheOperationInvoker;
import org.springframework.cache.jcache.interceptor.JCacheAspectSupport;
/**
* Concrete AspectJ cache aspect using JSR-107 standard annotations.
*
* <p>When using this aspect, you <i>must</i> annotate the implementation class (and/or
* methods within that class), <i>not</i> the interface (if any) that the class
* implements. AspectJ follows Java's rule that annotations on interfaces are <i>not</i>
* inherited.
*
* <p>Any method may be annotated (regardless of visibility). Annotating non-public
* methods directly is the only way to get caching demarcation for the execution of
* such operations.
*
* @author Stephane Nicoll
* @since 4.1
*/
public aspect JCacheCacheAspect extends JCacheAspectSupport {
@SuppressAjWarnings("adviceDidNotMatch")
Object around(final Object cachedObject) : cacheMethodExecution(cachedObject) {
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method method = methodSignature.getMethod();
CacheOperationInvoker aspectJInvoker = new CacheOperationInvoker() {
public Object invoke() {
try {
return proceed(cachedObject);
} catch (Throwable ex) {
throw new ThrowableWrapper(ex);
}
}
};
try {
return execute(aspectJInvoker, thisJoinPoint.getTarget(), method, thisJoinPoint.getArgs());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
if (th.getOriginal() instanceof RuntimeException) {
throw (RuntimeException) th.getOriginal();
}
throw th; // Lose original checked exception
}
}
/**
* Definition of pointcut: matched join points will have JSR-107
* cache management applied.
*/
protected pointcut cacheMethodExecution(Object cachedObject) :
(executionOfCacheResultMethod()
|| executionOfCachePutMethod()
|| executionOfCacheRemoveMethod()
|| executionOfCacheRemoveAllMethod())
&& this(cachedObject);
/**
* Matches the execution of any method with the @{@link CacheResult} annotation.
*/
private pointcut executionOfCacheResultMethod() :
execution(@CacheResult * *(..));
/**
* Matches the execution of any method with the @{@link CachePut} annotation.
*/
private pointcut executionOfCachePutMethod() :
execution(@CachePut * *(..));
/**
* Matches the execution of any method with the @{@link CacheRemove} annotation.
*/
private pointcut executionOfCacheRemoveMethod() :
execution(@CacheRemove * *(..));
/**
* Matches the execution of any method with the @{@link CacheRemoveAll} annotation.
*/
private pointcut executionOfCacheRemoveAllMethod() :
execution(@CacheRemoveAll * *(..));
}