SPR-7308
+ initial commit of caching abstraction + main API + Spring AOP and AspectJ support + annotation driven, declarative support + initial namespace draft git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3789 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
334e4eb1e7
commit
5c534c3757
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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 java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.aspectj.lang.annotation.SuppressAjWarnings;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.cache.interceptor.CacheAspectSupport;
|
||||||
|
import org.springframework.cache.interceptor.CacheDefinitionSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract superaspect for AspectJ cache aspects. Concrete
|
||||||
|
* subaspects will implement the <code>cacheMethodExecution()</code>
|
||||||
|
* pointcut using a strategy such as Java 5 annotations.
|
||||||
|
*
|
||||||
|
* <p>Suitable for use inside or outside the Spring IoC container.
|
||||||
|
* Set the "cacheManager" property appropriately, allowing
|
||||||
|
* use of any cache implementation supported by Spring.
|
||||||
|
*
|
||||||
|
* <p><b>NB:</b> If a method implements an interface that is itself
|
||||||
|
* cache annotated, the relevant Spring cache definition
|
||||||
|
* will <i>not</i> be resolved.
|
||||||
|
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
|
||||||
|
|
||||||
|
protected AbstractCacheAspect() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct object using the given caching metadata retrieval strategy.
|
||||||
|
* @param cds {@link CacheDefinitionSource} implementation, retrieving Spring
|
||||||
|
* cache metadata for each joinpoint.
|
||||||
|
*/
|
||||||
|
protected AbstractCacheAspect(CacheDefinitionSource... cds) {
|
||||||
|
setCacheDefinitionSources(cds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressAjWarnings("adviceDidNotMatch")
|
||||||
|
Object around(final Object cachedObject) : cacheMethodExecution(cachedObject){
|
||||||
|
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
|
||||||
|
Method method = methodSignature.getMethod();
|
||||||
|
|
||||||
|
Callable<Object> ajInvocation = new Callable<Object>() {
|
||||||
|
|
||||||
|
public Object call() {
|
||||||
|
return proceed(cachedObject);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return execute(ajInvocation, thisJoinPoint.getTarget(), method, thisJoinPoint.getArgs());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new UnsupportedOperationException("Should not throw exception", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concrete subaspects must implement this pointcut, to identify
|
||||||
|
* cached methods. For each selected joinpoint, {@link CacheOperationDefinition}
|
||||||
|
* will be retrieved using Spring's {@link CacheDefinitionSource} interface.
|
||||||
|
*/
|
||||||
|
protected abstract pointcut cacheMethodExecution(Object cachedObject);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.cache.annotation.AnnotationCacheDefinitionSource;
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concrete AspectJ cache aspect using Spring {@link Cacheable} annotation
|
||||||
|
* for JDK 1.5+.
|
||||||
|
*
|
||||||
|
* <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>A {@link Cacheable} annotation on a class specifies the default caching
|
||||||
|
* semantics for the execution of any <b>public</b> operation in the class.
|
||||||
|
*
|
||||||
|
* <p>A {@link Cacheable} annotation on a method within the class overrides the
|
||||||
|
* default caching semantics given by the class annotation (if present).
|
||||||
|
* 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 Costin Leau
|
||||||
|
*/
|
||||||
|
public aspect AnnotationCacheAspect extends AbstractCacheAspect {
|
||||||
|
|
||||||
|
public AnnotationCacheAspect() {
|
||||||
|
super(new AnnotationCacheDefinitionSource(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the execution of any public method in a type with the
|
||||||
|
* {@link Cacheable} annotation, or any subtype of a type with the
|
||||||
|
* {@link Cacheable} annotation.
|
||||||
|
*/
|
||||||
|
private pointcut executionOfAnyPublicMethodInAtCacheableType() :
|
||||||
|
execution(public * ((@Cacheable *)+).*(..)) && @this(Cacheable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the execution of any public method in a type with the
|
||||||
|
* {@link CacheEvict} annotation, or any subtype of a type with the
|
||||||
|
* {@link CacheEvict} annotation.
|
||||||
|
*/
|
||||||
|
private pointcut executionOfAnyPublicMethodInAtCacheEvictType() :
|
||||||
|
execution(public * ((@CacheEvict *)+).*(..)) && @this(CacheEvict);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the execution of any method with the
|
||||||
|
* Cacheable annotation.
|
||||||
|
*/
|
||||||
|
private pointcut executionOfCacheableMethod() :
|
||||||
|
execution(* *(..)) && @annotation(Cacheable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the execution of any method with the {@link CacheEvict} annotation.
|
||||||
|
*/
|
||||||
|
private pointcut executionOfCacheEvictMethod() :
|
||||||
|
execution(* *(..)) && @annotation(CacheEvict);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition of pointcut from super aspect - matched join points
|
||||||
|
* will have Spring cache management applied.
|
||||||
|
*/
|
||||||
|
protected pointcut cacheMethodExecution(Object cachedObject) :
|
||||||
|
(executionOfAnyPublicMethodInAtCacheableType() || executionOfAnyPublicMethodInAtCacheEvictType()
|
||||||
|
|| executionOfCacheableMethod() || executionOfCacheEvictMethod())
|
||||||
|
&& this(cachedObject);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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 static org.junit.Assert.assertNotSame;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.cache.config.CacheableService;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract annotation test (containing several reusable methods).
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public abstract class AbstractAnnotationTest {
|
||||||
|
|
||||||
|
protected ApplicationContext ctx;
|
||||||
|
|
||||||
|
protected CacheableService cs;
|
||||||
|
|
||||||
|
protected CacheableService ccs;
|
||||||
|
|
||||||
|
protected abstract String getConfig();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
ctx = new ClassPathXmlApplicationContext(getConfig());
|
||||||
|
cs = ctx.getBean("service", CacheableService.class);
|
||||||
|
ccs = ctx.getBean("classService", CacheableService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCacheable(CacheableService service) throws Exception {
|
||||||
|
Object o1 = new Object();
|
||||||
|
Object o2 = new Object();
|
||||||
|
|
||||||
|
Object r1 = service.cache(o1);
|
||||||
|
Object r2 = service.cache(o1);
|
||||||
|
Object r3 = service.cache(o1);
|
||||||
|
|
||||||
|
assertSame(r1, r2);
|
||||||
|
assertSame(r1, r3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidate(CacheableService service) throws Exception {
|
||||||
|
Object o1 = new Object();
|
||||||
|
Object o2 = new Object();
|
||||||
|
|
||||||
|
Object r1 = service.cache(o1);
|
||||||
|
Object r2 = service.cache(o1);
|
||||||
|
|
||||||
|
assertSame(r1, r2);
|
||||||
|
service.invalidate(o1);
|
||||||
|
Object r3 = service.cache(o1);
|
||||||
|
Object r4 = service.cache(o1);
|
||||||
|
assertNotSame(r1, r3);
|
||||||
|
assertSame(r3, r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConditionalExpression(CacheableService service) throws Exception {
|
||||||
|
Object r1 = service.conditional(4);
|
||||||
|
Object r2 = service.conditional(4);
|
||||||
|
|
||||||
|
assertNotSame(r1, r2);
|
||||||
|
|
||||||
|
Object r3 = service.conditional(3);
|
||||||
|
Object r4 = service.conditional(3);
|
||||||
|
|
||||||
|
assertSame(r3, r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testKeyExpression(CacheableService service) throws Exception {
|
||||||
|
Object r1 = service.key(5, 1);
|
||||||
|
Object r2 = service.key(5, 2);
|
||||||
|
|
||||||
|
assertSame(r1, r2);
|
||||||
|
|
||||||
|
Object r3 = service.key(1, 5);
|
||||||
|
Object r4 = service.key(2, 5);
|
||||||
|
|
||||||
|
assertNotSame(r3, r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCacheable() throws Exception {
|
||||||
|
testCacheable(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidate() throws Exception {
|
||||||
|
testInvalidate(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConditionalExpression() throws Exception {
|
||||||
|
testConditionalExpression(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKeyExpression() throws Exception {
|
||||||
|
testKeyExpression(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassCacheCacheable() throws Exception {
|
||||||
|
testCacheable(ccs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassCacheInvalidate() throws Exception {
|
||||||
|
testInvalidate(ccs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class AspectJAnnotationTest extends AbstractAnnotationTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getConfig() {
|
||||||
|
return "/org/springframework/cache/config/annotation-cache-aspectj.xml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@Cacheable
|
||||||
|
public class AnnotatedClassCacheableService implements CacheableService {
|
||||||
|
|
||||||
|
private AtomicLong counter = new AtomicLong();
|
||||||
|
|
||||||
|
public Object cache(Object arg1) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object conditional(int field) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict
|
||||||
|
public void invalidate(Object arg1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(key = "#p0")
|
||||||
|
public Object key(Object arg1, Object arg2) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
34
org.springframework.aspects/src/test/java/org/springframework/cache/config/CacheableService.java
vendored
Normal file
34
org.springframework.aspects/src/test/java/org/springframework/cache/config/CacheableService.java
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic service interface.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheableService<T> {
|
||||||
|
|
||||||
|
T cache(Object arg1);
|
||||||
|
|
||||||
|
void invalidate(Object arg1);
|
||||||
|
|
||||||
|
T conditional(int field);
|
||||||
|
|
||||||
|
T key(Object arg1, Object arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple cacheable service
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class DefaultCacheableService implements CacheableService<Long> {
|
||||||
|
|
||||||
|
private AtomicLong counter = new AtomicLong();
|
||||||
|
|
||||||
|
@Cacheable
|
||||||
|
public Long cache(Object arg1) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict
|
||||||
|
public void invalidate(Object arg1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(condition = "#classField == 3")
|
||||||
|
public Long conditional(int classField) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(key = "#p0")
|
||||||
|
public Long key(Object arg1, Object arg2) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||||
|
xmlns:p="http://www.springframework.org/schema/p"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
|
||||||
|
|
||||||
|
<bean id="annotationSource" class="org.springframework.cache.annotation.AnnotationCacheDefinitionSource"/>
|
||||||
|
|
||||||
|
<aop:config>
|
||||||
|
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/>
|
||||||
|
</aop:config>
|
||||||
|
|
||||||
|
<bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
|
||||||
|
<property name="cacheManager" ref="cacheManager"/>
|
||||||
|
<property name="cacheDefinitionSources" ref="annotationSource"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<bean id="advisor" class="org.springframework.cache.interceptor.BeanFactoryCacheDefinitionSourceAdvisor">
|
||||||
|
<property name="cacheDefinitionSource" ref="annotationSource"/>
|
||||||
|
<property name="adviceBeanName" value="cacheAspect"/>
|
||||||
|
</bean>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<bean id="cacheManager" class="org.springframework.cache.support.MapCacheManager">
|
||||||
|
<property name="caches">
|
||||||
|
<set>
|
||||||
|
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/>
|
||||||
|
</set>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
|
||||||
|
|
||||||
|
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>
|
||||||
|
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/>
|
||||||
|
|
||||||
|
</beans>
|
||||||
200
org.springframework.context.support/src/main/java/org/springframework/cache/Cache.java
vendored
Normal file
200
org.springframework.context.support/src/main/java/org/springframework/cache/Cache.java
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that defines the common cache operations.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface Cache<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cache name.
|
||||||
|
*
|
||||||
|
* @return the cache name.
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the the native, underlying cache provider.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Object getNativeCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <tt>true</tt> if this cache contains a mapping for the specified
|
||||||
|
* key. More formally, returns <tt>true</tt> if and only if
|
||||||
|
* this cache contains a mapping for a key <tt>k</tt> such that
|
||||||
|
* <tt>(key==null ? k==null : key.equals(k))</tt>. (There can be
|
||||||
|
* at most one such mapping.)
|
||||||
|
*
|
||||||
|
* @param key key whose presence in this cache is to be tested.
|
||||||
|
* @return <tt>true</tt> if this cache contains a mapping for the specified
|
||||||
|
* key.
|
||||||
|
*/
|
||||||
|
boolean containsKey(Object key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value to which this cache maps the specified key. Returns
|
||||||
|
* <tt>null</tt> if the cache contains no mapping for this key. A return
|
||||||
|
* value of <tt>null</tt> does not <i>necessarily</i> indicate that the
|
||||||
|
* cache contains no mapping for the key; it's also possible that the cache
|
||||||
|
* explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
|
||||||
|
* operation may be used to distinguish these two cases.
|
||||||
|
*
|
||||||
|
* <p>More formally, if this cache contains a mapping from a key
|
||||||
|
* <tt>k</tt> to a value <tt>v</tt> such that <tt>(key==null ? k==null :
|
||||||
|
* key.equals(k))</tt>, then this method returns <tt>v</tt>; otherwise
|
||||||
|
* it returns <tt>null</tt>. (There can be at most one such mapping.)
|
||||||
|
*
|
||||||
|
* @param key key whose associated value is to be returned.
|
||||||
|
* @return the value to which this cache maps the specified key, or
|
||||||
|
* <tt>null</tt> if the cache contains no mapping for this key.
|
||||||
|
*
|
||||||
|
* @see #containsKey(Object)
|
||||||
|
*/
|
||||||
|
V get(Object key);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates the specified value with the specified key in this cache
|
||||||
|
* (optional operation). If the cache previously contained a mapping for
|
||||||
|
* this key, the old value is replaced by the specified value. (A cache
|
||||||
|
* <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
|
||||||
|
* if {@link #containsKey(Object) m.containsKey(k)} would return
|
||||||
|
* <tt>true</tt>.))
|
||||||
|
*
|
||||||
|
* @param key key with which the specified value is to be associated.
|
||||||
|
* @param value value to be associated with the specified key.
|
||||||
|
* @return previous value associated with specified key, or <tt>null</tt>
|
||||||
|
* if there was no mapping for key. A <tt>null</tt> return can
|
||||||
|
* also indicate that the cache previously associated <tt>null</tt>
|
||||||
|
* with the specified key, if the implementation supports
|
||||||
|
* <tt>null</tt> values.
|
||||||
|
*/
|
||||||
|
V put(K key, V value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the specified key is not already associated with a value, associate it with the given value.
|
||||||
|
*
|
||||||
|
* This is equivalent to:
|
||||||
|
* <pre>
|
||||||
|
* if (!cache.containsKey(key))
|
||||||
|
* return cache.put(key, value);
|
||||||
|
* else
|
||||||
|
* return cache.get(key);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param key key with which the specified value is to be associated.
|
||||||
|
* @param value value to be associated with the specified key.
|
||||||
|
* @return previous value associated with specified key, or <tt>null</tt>
|
||||||
|
* if there was no mapping for key. A <tt>null</tt> return can
|
||||||
|
* also indicate that the cache previously associated <tt>null</tt>
|
||||||
|
* with the specified key, if the implementation supports
|
||||||
|
* <tt>null</tt> values.
|
||||||
|
*/
|
||||||
|
V putIfAbsent(K key, V value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the mapping for this key from this cache if it is present
|
||||||
|
* (optional operation). More formally, if this cache contains a mapping
|
||||||
|
* from key <tt>k</tt> to value <tt>v</tt> such that
|
||||||
|
* <code>(key==null ? k==null : key.equals(k))</code>, that mapping
|
||||||
|
* is removed. (The cache can contain at most one such mapping.)
|
||||||
|
*
|
||||||
|
* <p>Returns the value to which the cache previously associated the key, or
|
||||||
|
* <tt>null</tt> if the cache contained no mapping for this key. (A
|
||||||
|
* <tt>null</tt> return can also indicate that the cache previously
|
||||||
|
* associated <tt>null</tt> with the specified key if the implementation
|
||||||
|
* supports <tt>null</tt> values.) The cache will not contain a mapping for
|
||||||
|
* the specified key once the call returns.
|
||||||
|
*
|
||||||
|
* @param key key whose mapping is to be removed from the cache.
|
||||||
|
* @return previous value associated with specified key, or <tt>null</tt>
|
||||||
|
* if there was no mapping for key.
|
||||||
|
*/
|
||||||
|
V remove(Object key);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove entry for key only if currently mapped to given value.
|
||||||
|
*
|
||||||
|
* Similar to:
|
||||||
|
* <pre>
|
||||||
|
* if ((cache.containsKey(key) && cache.get(key).equals(value)) {
|
||||||
|
* cache.remove(key);
|
||||||
|
* return true;
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* return false;
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param key key with which the specified value is associated.
|
||||||
|
* @param value value associated with the specified key.
|
||||||
|
* @return true if the value was removed, false otherwise
|
||||||
|
*/
|
||||||
|
boolean remove(Object key, Object value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace entry for key only if currently mapped to given value.
|
||||||
|
*
|
||||||
|
* Similar to:
|
||||||
|
* <pre>
|
||||||
|
* if ((cache.containsKey(key) && cache.get(key).equals(oldValue)) {
|
||||||
|
* cache.put(key, newValue);
|
||||||
|
* return true;
|
||||||
|
* } else return false;
|
||||||
|
* </pre>
|
||||||
|
|
||||||
|
* @param key key with which the specified value is associated.
|
||||||
|
* @param oldValue value expected to be associated with the specified key.
|
||||||
|
* @param newValue value to be associated with the specified key.
|
||||||
|
* @return true if the value was replaced
|
||||||
|
*/
|
||||||
|
boolean replace(K key, V oldValue, V newValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace entry for key only if currently mapped to some value.
|
||||||
|
* Acts as
|
||||||
|
* <pre>
|
||||||
|
* if ((cache.containsKey(key)) {
|
||||||
|
* return cache.put(key, value);
|
||||||
|
* } else return null;
|
||||||
|
* </pre>
|
||||||
|
* except that the action is performed atomically.
|
||||||
|
* @param key key with which the specified value is associated.
|
||||||
|
* @param value value to be associated with the specified key.
|
||||||
|
* @return previous value associated with specified key, or <tt>null</tt>
|
||||||
|
* if there was no mapping for key. A <tt>null</tt> return can
|
||||||
|
* also indicate that the cache previously associated <tt>null</tt>
|
||||||
|
* with the specified key, if the implementation supports
|
||||||
|
* <tt>null</tt> values.
|
||||||
|
*/
|
||||||
|
V replace(K key, V value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all mappings from the cache.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
}
|
||||||
43
org.springframework.context.support/src/main/java/org/springframework/cache/CacheManager.java
vendored
Normal file
43
org.springframework.context.support/src/main/java/org/springframework/cache/CacheManager.java
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity managing {@link Cache}s.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cache associated with the given name.
|
||||||
|
*
|
||||||
|
* @param name cache identifier - cannot be null
|
||||||
|
* @return associated cache or null if none is found
|
||||||
|
*/
|
||||||
|
<K, V> Cache<K, V> getCache(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of the caches known by this cache manager.
|
||||||
|
*
|
||||||
|
* @return names of caches known by the cache manager.
|
||||||
|
*/
|
||||||
|
Collection<String> getCacheNames();
|
||||||
|
}
|
||||||
29
org.springframework.context.support/src/main/java/org/springframework/cache/KeyGenerator.java
vendored
Normal file
29
org.springframework.context.support/src/main/java/org/springframework/cache/KeyGenerator.java
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache 'key' extractor. Used for creating a key based on the given
|
||||||
|
* parameters.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
// CL: could be renamed to KeyFactory
|
||||||
|
public interface KeyGenerator<K> {
|
||||||
|
|
||||||
|
K extract(Object... params);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.annotation;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.cache.interceptor.AbstractFallbackCacheDefinitionSource;
|
||||||
|
import org.springframework.cache.interceptor.CacheDefinition;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Implementation of the
|
||||||
|
* {@link org.springframework.cache.interceptor.CacheDefinitionSource}
|
||||||
|
* interface for working with caching metadata in JDK 1.5+ annotation format.
|
||||||
|
*
|
||||||
|
* <p>This class reads Spring's JDK 1.5+ {@link Cacheable} and {@link CacheEvict}
|
||||||
|
* annotations and
|
||||||
|
* exposes corresponding caching operation definition to Spring's cache infrastructure.
|
||||||
|
* This class may also serve as base class for a custom CacheDefinitionSource.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class AnnotationCacheDefinitionSource extends AbstractFallbackCacheDefinitionSource implements
|
||||||
|
Serializable {
|
||||||
|
|
||||||
|
private final boolean publicMethodsOnly;
|
||||||
|
|
||||||
|
private final Set<CacheAnnotationParser> annotationParsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a default AnnotationCacheOperationDefinitionSource, supporting
|
||||||
|
* public methods that carry the <code>Cacheable</code> and <code>CacheInvalidate</code>
|
||||||
|
* annotations.
|
||||||
|
*/
|
||||||
|
public AnnotationCacheDefinitionSource() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a custom AnnotationCacheOperationDefinitionSource, supporting
|
||||||
|
* public methods that carry the <code>Cacheable</code> and
|
||||||
|
* <code>CacheInvalidate</code> annotations.
|
||||||
|
*
|
||||||
|
* @param publicMethodsOnly whether to support only annotated public methods
|
||||||
|
* typically for use with proxy-based AOP), or protected/private methods as well
|
||||||
|
* (typically used with AspectJ class weaving)
|
||||||
|
*/
|
||||||
|
public AnnotationCacheDefinitionSource(boolean publicMethodsOnly) {
|
||||||
|
this.publicMethodsOnly = publicMethodsOnly;
|
||||||
|
this.annotationParsers = new LinkedHashSet<CacheAnnotationParser>(1);
|
||||||
|
this.annotationParsers.add(new SpringCachingAnnotationParser());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a custom AnnotationCacheOperationDefinitionSource.
|
||||||
|
* @param annotationParsers the CacheAnnotationParser to use
|
||||||
|
*/
|
||||||
|
public AnnotationCacheDefinitionSource(CacheAnnotationParser... annotationParsers) {
|
||||||
|
this.publicMethodsOnly = true;
|
||||||
|
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
|
||||||
|
Set<CacheAnnotationParser> parsers = new LinkedHashSet<CacheAnnotationParser>(annotationParsers.length);
|
||||||
|
Collections.addAll(parsers, annotationParsers);
|
||||||
|
this.annotationParsers = parsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CacheDefinition findCacheDefinition(Class<?> clazz) {
|
||||||
|
return determineCacheDefinition(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CacheDefinition findCacheOperation(Method method) {
|
||||||
|
return determineCacheDefinition(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the cache operation definition for the given method or class.
|
||||||
|
* <p>This implementation delegates to configured
|
||||||
|
* {@link CacheAnnotationParser CacheAnnotationParsers}
|
||||||
|
* for parsing known annotations into Spring's metadata attribute class.
|
||||||
|
* Returns <code>null</code> if it's not cacheable.
|
||||||
|
* <p>Can be overridden to support custom annotations that carry caching metadata.
|
||||||
|
* @param ae the annotated method or class
|
||||||
|
* @return CacheOperationDefinition the configured caching operation,
|
||||||
|
* or <code>null</code> if none was found
|
||||||
|
*/
|
||||||
|
protected CacheDefinition determineCacheDefinition(AnnotatedElement ae) {
|
||||||
|
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
|
||||||
|
CacheDefinition attr = annotationParser.parseTransactionAnnotation(ae);
|
||||||
|
if (attr != null) {
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, only public methods can be made cacheable.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean allowPublicMethodsOnly() {
|
||||||
|
return this.publicMethodsOnly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.annotation;
|
||||||
|
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
|
||||||
|
import org.springframework.cache.interceptor.CacheDefinition;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy interface for parsing known caching annotation types.
|
||||||
|
* {@link AnnotationCacheDefinitionSource} delegates to such
|
||||||
|
* parsers for supporting specific annotation types such as Spring's own
|
||||||
|
* {@link Cacheable} or {@link CacheEvict}.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheAnnotationParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the cache definition for the given method or class,
|
||||||
|
* based on a known annotation type.
|
||||||
|
* <p>This essentially parses a known cache annotation into Spring's
|
||||||
|
* metadata attribute class. Returns <code>null</code> if the method/class
|
||||||
|
* is not cacheable.
|
||||||
|
* @param ae the annotated method or class
|
||||||
|
* @return CacheOperationDefinition the configured caching operation,
|
||||||
|
* or <code>null</code> if none was found
|
||||||
|
* @see AnnotationCacheDefinitionSource#determineCacheOperationDefinition
|
||||||
|
*/
|
||||||
|
CacheDefinition parseTransactionAnnotation(AnnotatedElement ae);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation indicating that a method (or all methods on a class) trigger(s)
|
||||||
|
* a cache invalidate operation.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@Target( { ElementType.METHOD, ElementType.TYPE })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
@Documented
|
||||||
|
public @interface CacheEvict {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Qualifier value for the specified cached operation.
|
||||||
|
* <p>May be used to determine the target cache (or caches), matching the qualifier
|
||||||
|
* value (or the bean name(s)) of (a) specific bean definition.
|
||||||
|
*/
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Expression Language (SpEL) attribute for computing the key dynamically.
|
||||||
|
* <p/>
|
||||||
|
* Default is "" meaning all method parameters are considered as a key.
|
||||||
|
*/
|
||||||
|
String key() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
|
||||||
|
* <p/>
|
||||||
|
* Default is "" meaning the method is always cached.
|
||||||
|
*/
|
||||||
|
String condition() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not all the entries inside the cache are removed or not.
|
||||||
|
* By default, only the value under the associated key is removed.
|
||||||
|
*
|
||||||
|
* Note that specifying setting this parameter to true and specifying a
|
||||||
|
* {@link CacheKey key} is not allowed.
|
||||||
|
*/
|
||||||
|
boolean allEntries() default false;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2009 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.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation indicating that a method (or all the methods on a class) can be cached.
|
||||||
|
* The method arguments and signature are used for computing the key while the return instance
|
||||||
|
* as the cache value.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@Target( { ElementType.METHOD, ElementType.TYPE })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
@Documented
|
||||||
|
public @interface Cacheable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the cache in which the update takes place.
|
||||||
|
* <p>May be used to determine the target cache (or caches), matching the qualifier
|
||||||
|
* value (or the bean name(s)) of (a) specific bean definition.
|
||||||
|
*/
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Expression Language (SpEL) attribute for computing the key dynamically.
|
||||||
|
* <p/>
|
||||||
|
* Default is "" meaning all method parameters are considered as a key.
|
||||||
|
*/
|
||||||
|
String key() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
|
||||||
|
* <p/>
|
||||||
|
* Default is "" meaning the method is always cached.
|
||||||
|
*/
|
||||||
|
String condition() default "";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.annotation;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
|
||||||
|
import org.springframework.cache.interceptor.CacheInvalidateDefinition;
|
||||||
|
import org.springframework.cache.interceptor.CacheDefinition;
|
||||||
|
import org.springframework.cache.interceptor.CacheUpdateDefinition;
|
||||||
|
import org.springframework.cache.interceptor.DefaultCacheInvalidateDefinition;
|
||||||
|
import org.springframework.cache.interceptor.DefaultCacheUpdateDefinition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy implementation for parsing Spring's {@link Cacheable} and {@link CacheEvict} annotations.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class SpringCachingAnnotationParser implements CacheAnnotationParser, Serializable {
|
||||||
|
|
||||||
|
public CacheDefinition parseTransactionAnnotation(AnnotatedElement ae) {
|
||||||
|
Cacheable update = findAnnotation(ae, Cacheable.class);
|
||||||
|
|
||||||
|
if (update != null) {
|
||||||
|
return parseCacheableAnnotation(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheEvict invalidate = findAnnotation(ae, CacheEvict.class);
|
||||||
|
|
||||||
|
if (invalidate != null) {
|
||||||
|
return parseInvalidateAnnotation(invalidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Annotation> T findAnnotation(AnnotatedElement ae, Class<T> annotationType) {
|
||||||
|
T ann = ae.getAnnotation(annotationType);
|
||||||
|
if (ann == null) {
|
||||||
|
for (Annotation metaAnn : ae.getAnnotations()) {
|
||||||
|
ann = metaAnn.annotationType().getAnnotation(annotationType);
|
||||||
|
if (ann != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ann;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheUpdateDefinition parseCacheableAnnotation(Cacheable ann) {
|
||||||
|
DefaultCacheUpdateDefinition dcud = new DefaultCacheUpdateDefinition();
|
||||||
|
dcud.setCacheName(ann.value());
|
||||||
|
dcud.setCondition(ann.condition());
|
||||||
|
dcud.setKey(ann.key());
|
||||||
|
|
||||||
|
return dcud;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheInvalidateDefinition parseInvalidateAnnotation(CacheEvict ann) {
|
||||||
|
DefaultCacheInvalidateDefinition dcid = new DefaultCacheInvalidateDefinition();
|
||||||
|
dcid.setCacheName(ann.value());
|
||||||
|
dcid.setCondition(ann.condition());
|
||||||
|
dcid.setKey(ann.key());
|
||||||
|
dcid.setCacheWide(ann.allEntries());
|
||||||
|
|
||||||
|
return dcid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.support.AbstractDelegatingCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple {@link Cache} implementation based on the JDK 1.5+ java.util.concurrent package.
|
||||||
|
* Useful for testing or simple caching scenarios.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class ConcurrentCache<K, V> extends AbstractDelegatingCache<K, V> {
|
||||||
|
|
||||||
|
private final ConcurrentMap<K, V> store;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public ConcurrentCache() {
|
||||||
|
this("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentCache(String name) {
|
||||||
|
this(new ConcurrentHashMap<K, V>(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentCache(ConcurrentMap<K, V> delegate, String name) {
|
||||||
|
super(delegate);
|
||||||
|
this.store = delegate;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentMap<K, V> getNativeCache() {
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V putIfAbsent(K key, V value) {
|
||||||
|
return store.putIfAbsent(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Object key, Object value) {
|
||||||
|
return store.remove(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean replace(K key, V oldValue, V newValue) {
|
||||||
|
return store.replace(key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V replace(K key, V value) {
|
||||||
|
return store.replace(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanNameAware;
|
||||||
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory bean for easy configuration of {@link ConcurrentCache} through Spring.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class ConcurrentCacheFactoryBean<K, V> implements FactoryBean<ConcurrentCache<K, V>>, BeanNameAware,
|
||||||
|
InitializingBean {
|
||||||
|
|
||||||
|
private String name = "";
|
||||||
|
private ConcurrentCache<K, V> cache;
|
||||||
|
|
||||||
|
private ConcurrentMap<K, V> store;
|
||||||
|
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
cache = (store == null ? new ConcurrentCache<K, V>(name) : new ConcurrentCache<K, V>(store, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentCache<K, V> getObject() throws Exception {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return (cache != null ? cache.getClass() : ConcurrentCache.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSingleton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBeanName(String beanName) {
|
||||||
|
setName(beanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStore(ConcurrentMap<K, V> store) {
|
||||||
|
this.store = store;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
import org.springframework.aop.config.AopNamespaceUtils;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
|
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||||
|
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||||
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
|
import org.springframework.cache.annotation.AnnotationCacheDefinitionSource;
|
||||||
|
import org.springframework.cache.interceptor.BeanFactoryCacheDefinitionSourceAdvisor;
|
||||||
|
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||||
|
* implementation that allows users to easily configure all the infrastructure
|
||||||
|
* beans required to enable annotation-driven cache demarcation.
|
||||||
|
*
|
||||||
|
* <p>By default, all proxies are created as JDK proxies. This may cause some
|
||||||
|
* problems if you are injecting objects as concrete classes rather than
|
||||||
|
* interfaces. To overcome this restriction you can set the
|
||||||
|
* '<code>proxy-target-class</code>' attribute to '<code>true</code>', which
|
||||||
|
* will result in class-based proxies being created.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
|
||||||
|
private static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager";
|
||||||
|
|
||||||
|
private static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bean name of the internally managed cache advisor (mode="proxy").
|
||||||
|
*/
|
||||||
|
public static final String CACHE_ADVISOR_BEAN_NAME = "org.springframework.cache.config.internalCacheAdvisor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bean name of the internally managed cache aspect (mode="aspectj").
|
||||||
|
*/
|
||||||
|
public static final String CACHE_ASPECT_BEAN_NAME = "org.springframework.cache.config.internalCacheAspect";
|
||||||
|
|
||||||
|
private static final String CACHE_ASPECT_CLASS_NAME = "org.springframework.cache.aspectj.AnnotationCacheAspect";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the '<code><cache:annotation-driven/></code>' tag. Will
|
||||||
|
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
|
||||||
|
* with the container as necessary.
|
||||||
|
*/
|
||||||
|
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||||
|
String mode = element.getAttribute("mode");
|
||||||
|
if ("aspectj".equals(mode)) {
|
||||||
|
// mode="aspectj"
|
||||||
|
registerCacheAspect(element, parserContext);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// mode="proxy"
|
||||||
|
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractCacheManager(Element element) {
|
||||||
|
return (element.hasAttribute(CACHE_MANAGER_ATTRIBUTE) ? element.getAttribute(CACHE_MANAGER_ATTRIBUTE)
|
||||||
|
: DEFAULT_CACHE_MANAGER_BEAN_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerCacheManagerProperty(Element element, BeanDefinition def) {
|
||||||
|
def.getPropertyValues().add("cacheManager", new RuntimeBeanReference(extractCacheManager(element)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a
|
||||||
|
* <pre>
|
||||||
|
* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
|
||||||
|
* <property name="cacheManagerBeanName" value="cacheManager"/>
|
||||||
|
* </bean>
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
* @param element
|
||||||
|
* @param parserContext
|
||||||
|
*/
|
||||||
|
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");
|
||||||
|
registerCacheManagerProperty(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.
|
||||||
|
*/
|
||||||
|
private static class AopAutoProxyConfigurer {
|
||||||
|
|
||||||
|
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
|
||||||
|
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
|
||||||
|
|
||||||
|
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ADVISOR_BEAN_NAME)) {
|
||||||
|
Object eleSource = parserContext.extractSource(element);
|
||||||
|
|
||||||
|
// Create the CacheDefinitionSource definition.
|
||||||
|
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationCacheDefinitionSource.class);
|
||||||
|
sourceDef.setSource(eleSource);
|
||||||
|
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
|
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||||
|
|
||||||
|
// Create the CacheInterceptor definition.
|
||||||
|
RootBeanDefinition interceptorDef = new RootBeanDefinition(CacheInterceptor.class);
|
||||||
|
interceptorDef.setSource(eleSource);
|
||||||
|
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
|
registerCacheManagerProperty(element, interceptorDef);
|
||||||
|
interceptorDef.getPropertyValues().add("cacheDefinitionSources", new RuntimeBeanReference(sourceName));
|
||||||
|
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||||
|
|
||||||
|
// Create the CacheAdvisor definition.
|
||||||
|
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheDefinitionSourceAdvisor.class);
|
||||||
|
advisorDef.setSource(eleSource);
|
||||||
|
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
|
advisorDef.getPropertyValues().add("cacheDefinitionSource", new RuntimeBeanReference(sourceName));
|
||||||
|
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
|
||||||
|
if (element.hasAttribute("order")) {
|
||||||
|
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
|
||||||
|
}
|
||||||
|
parserContext.getRegistry().registerBeanDefinition(CACHE_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, CACHE_ADVISOR_BEAN_NAME));
|
||||||
|
parserContext.registerComponent(compositeDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>NamespaceHandler</code> allowing for the configuration of
|
||||||
|
* declarative cache management using either XML or using annotations.
|
||||||
|
*
|
||||||
|
* <p>This namespace handler is the central piece of functionality in the
|
||||||
|
* Spring cache management facilities.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class CacheNamespaceHandler extends NamespaceHandlerSupport {
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.ehcache;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.sf.ehcache.Ehcache;
|
||||||
|
import net.sf.ehcache.Element;
|
||||||
|
import net.sf.ehcache.Status;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.support.SimpleMapEntry;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Cache} implementation on top of an {@link Ehcache} instance.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class EhCacheCache implements Cache<Object, Object> {
|
||||||
|
|
||||||
|
private final Ehcache cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link EhCacheCache} instance.
|
||||||
|
*
|
||||||
|
* @param ehcache backing Ehcache instance
|
||||||
|
*/
|
||||||
|
public EhCacheCache(Ehcache ehcache) {
|
||||||
|
Assert.notNull(ehcache, "non null ehcache required");
|
||||||
|
Status status = ehcache.getStatus();
|
||||||
|
Assert.isTrue(Status.STATUS_ALIVE.equals(status), "an 'alive' ehcache is required - current cache is "
|
||||||
|
+ status.toString());
|
||||||
|
this.cache = ehcache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return cache.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Ehcache getNativeCache() {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
cache.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return cache.isKeyInCache(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return cache.isValueInCache(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||||
|
List<Object> keys = cache.getKeys();
|
||||||
|
Set<Map.Entry<Object, Object>> entries = new LinkedHashSet<Map.Entry<Object, Object>>(keys.size());
|
||||||
|
for (Object key : keys) {
|
||||||
|
Element element = cache.get(key);
|
||||||
|
if (element != null) {
|
||||||
|
entries.add(new SimpleMapEntry(key, element.getObjectValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableSet(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(Object key) {
|
||||||
|
Element element = cache.get(key);
|
||||||
|
return (element != null ? element.getObjectValue() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return cache.getSize() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Set<Object> keySet() {
|
||||||
|
List<Object> keys = cache.getKeys();
|
||||||
|
Set<Object> keySet = new LinkedHashSet<Object>(keys.size());
|
||||||
|
for (Object key : keys) {
|
||||||
|
keySet.add(key);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(keySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object put(Object key, Object value) {
|
||||||
|
Element previous = cache.getQuiet(key);
|
||||||
|
cache.put(new Element(key, value));
|
||||||
|
return (previous != null ? previous.getValue() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putAll(Map<? extends Object, ? extends Object> m) {
|
||||||
|
for (Map.Entry<? extends Object, ? extends Object> entry : m.entrySet()) {
|
||||||
|
cache.put(new Element(entry.getKey(), entry.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object remove(Object key) {
|
||||||
|
Object value = null;
|
||||||
|
if (cache.isKeyInCache(key)) {
|
||||||
|
Element element = cache.getQuiet(key);
|
||||||
|
value = (element != null ? element.getObjectValue() : null);
|
||||||
|
}
|
||||||
|
cache.remove(key);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return cache.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Collection<Object> values() {
|
||||||
|
List<Object> keys = cache.getKeys();
|
||||||
|
List<Object> values = new ArrayList<Object>(keys.size());
|
||||||
|
for (Object key : keys) {
|
||||||
|
Element element = cache.get(key);
|
||||||
|
if (element != null) {
|
||||||
|
values.add(element.getObjectValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableCollection(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object putIfAbsent(Object key, Object value) {
|
||||||
|
// putIfAbsent supported only from Ehcache 2.1
|
||||||
|
// return cache.putIfAbsent(new Element(key, value));
|
||||||
|
Element existing = cache.getQuiet(key);
|
||||||
|
if (existing == null) {
|
||||||
|
cache.put(new Element(key, value));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return existing.getObjectValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Object key, Object value) {
|
||||||
|
// remove(Element) supported only from Ehcache 2.1
|
||||||
|
// return cache.removeElement(new Element(key, value));
|
||||||
|
Element existing = cache.getQuiet(key);
|
||||||
|
|
||||||
|
if (existing != null && existing.getObjectValue().equals(value)) {
|
||||||
|
cache.remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object replace(Object key, Object value) {
|
||||||
|
// replace(Object, Object) supported only from Ehcache 2.1
|
||||||
|
// return cache.replace(new Element(key, value));
|
||||||
|
Element existing = cache.getQuiet(key);
|
||||||
|
|
||||||
|
if (existing != null) {
|
||||||
|
cache.put(new Element(key, value));
|
||||||
|
return existing.getObjectValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean replace(Object key, Object oldValue, Object newValue) {
|
||||||
|
// replace(Object, Object, Object) supported only from Ehcache 2.1
|
||||||
|
// return cache.replace(new Element(key, oldValue), new Element(key,
|
||||||
|
// newValue));
|
||||||
|
Element existing = cache.getQuiet(key);
|
||||||
|
|
||||||
|
if (existing != null && existing.getObjectValue().equals(oldValue)) {
|
||||||
|
cache.put(new Element(key, newValue));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.ehcache;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
|
import net.sf.ehcache.Ehcache;
|
||||||
|
import net.sf.ehcache.Status;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.support.AbstractCacheManager;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CacheManager backed by an Ehcache {@link net.sf.ehcache.CacheManager}.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class EhcacheCacheManager extends AbstractCacheManager {
|
||||||
|
|
||||||
|
private net.sf.ehcache.CacheManager cacheManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Cache<?, ?>> loadCaches() {
|
||||||
|
Assert.notNull(cacheManager, "a backing Ehcache cache manager is required");
|
||||||
|
Status status = cacheManager.getStatus();
|
||||||
|
|
||||||
|
Assert.isTrue(Status.STATUS_ALIVE.equals(status),
|
||||||
|
"an 'alive' Ehcache cache manager is required - current cache is " + status.toString());
|
||||||
|
|
||||||
|
String[] names = cacheManager.getCacheNames();
|
||||||
|
Collection<Cache<?, ?>> caches = new LinkedHashSet<Cache<?, ?>>(names.length);
|
||||||
|
|
||||||
|
for (String name : names) {
|
||||||
|
caches.add(new EhCacheCache(cacheManager.getEhcache(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return caches;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <K, V> Cache<K, V> getCache(String name) {
|
||||||
|
Cache cache = super.getCache(name);
|
||||||
|
if (cache == null) {
|
||||||
|
// check the Ehcache cache again
|
||||||
|
// in case the cache was added at runtime
|
||||||
|
|
||||||
|
Ehcache ehcache = cacheManager.getEhcache(name);
|
||||||
|
if (ehcache != null) {
|
||||||
|
// reinitialize cache map
|
||||||
|
afterPropertiesSet();
|
||||||
|
cache = super.getCache(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the backing Ehcache {@link net.sf.ehcache.CacheManager}.
|
||||||
|
*
|
||||||
|
* @param cacheManager backing Ehcache {@link net.sf.ehcache.CacheManager}
|
||||||
|
*/
|
||||||
|
public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
|
||||||
|
this.cacheManager = cacheManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class implementing {@link CacheDefinition}.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
abstract class AbstractCacheDefinition implements CacheDefinition {
|
||||||
|
|
||||||
|
private String cacheName = "";
|
||||||
|
private String condition = "";
|
||||||
|
private String key = "";
|
||||||
|
private String name = "";
|
||||||
|
|
||||||
|
|
||||||
|
public String getCacheName() {
|
||||||
|
return cacheName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCondition() {
|
||||||
|
return condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCacheName(String cacheName) {
|
||||||
|
this.cacheName = cacheName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCondition(String condition) {
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation compares the <code>toString()</code> results.
|
||||||
|
* @see #toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return (other instanceof CacheDefinition && toString().equals(other.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation returns <code>toString()</code>'s hash code.
|
||||||
|
* @see #toString()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return toString().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an identifying description for this cache operation definition.
|
||||||
|
* <p>Has to be overridden in subclasses for correct <code>equals</code>
|
||||||
|
* and <code>hashCode</code> behavior. Alternatively, {@link #equals}
|
||||||
|
* and {@link #hashCode} can be overridden themselves.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getDefinitionDescription().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an identifying description for this caching definition.
|
||||||
|
* <p>Available to subclasses, for inclusion in their <code>toString()</code> result.
|
||||||
|
*/
|
||||||
|
protected StringBuilder getDefinitionDescription() {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.append(cacheName);
|
||||||
|
result.append(',');
|
||||||
|
result.append(condition);
|
||||||
|
result.append(",");
|
||||||
|
result.append(key);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
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 CacheDefinition} 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.
|
||||||
|
*
|
||||||
|
* <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
|
||||||
|
* @see org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource
|
||||||
|
*/
|
||||||
|
public abstract class AbstractFallbackCacheDefinitionSource implements CacheDefinitionSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canonical value held in cache to indicate no caching attribute was
|
||||||
|
* found for this method, and we don't need to look again.
|
||||||
|
*/
|
||||||
|
private final static CacheDefinition NULL_CACHING_ATTRIBUTE = new DefaultCacheUpdateDefinition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger available to subclasses.
|
||||||
|
* <p>As this base class is not marked Serializable, the logger will be recreated
|
||||||
|
* after serialization - provided that the concrete subclass is Serializable.
|
||||||
|
*/
|
||||||
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache of CacheOperationDefinitions, keyed by DefaultCacheKey (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, CacheDefinition> attributeCache = new ConcurrentHashMap<Object, CacheDefinition>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the caching attribute for this method invocation.
|
||||||
|
* <p>Defaults to the class's caching attribute if no method attribute is found.
|
||||||
|
* @param method the method for the current invocation (never <code>null</code>)
|
||||||
|
* @param targetClass the target class for this invocation (may be <code>null</code>)
|
||||||
|
* @return {@link CacheDefinition} for this method, or <code>null</code> if the method
|
||||||
|
* is not cacheable
|
||||||
|
*/
|
||||||
|
public CacheDefinition getCacheDefinition(Method method, Class<?> targetClass) {
|
||||||
|
// First, see if we have a cached value.
|
||||||
|
Object cacheKey = getCacheKey(method, targetClass);
|
||||||
|
CacheDefinition cached = this.attributeCache.get(cacheKey);
|
||||||
|
if (cached != null) {
|
||||||
|
if (cached == NULL_CACHING_ATTRIBUTE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Value will either be canonical value indicating there is no caching attribute,
|
||||||
|
// or an actual caching attribute.
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// We need to work it out.
|
||||||
|
CacheDefinition cacheDef = computeCacheOperationDefinition(method, targetClass);
|
||||||
|
// Put it in the cache.
|
||||||
|
if (cacheDef == null) {
|
||||||
|
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheDef);
|
||||||
|
}
|
||||||
|
this.attributeCache.put(cacheKey, cacheDef);
|
||||||
|
}
|
||||||
|
return cacheDef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine a cache key for the given method and target class.
|
||||||
|
* <p>Must not produce same key for overloaded methods.
|
||||||
|
* Must produce same key for different instances of the same method.
|
||||||
|
* @param method the method (never <code>null</code>)
|
||||||
|
* @param targetClass the target class (may be <code>null</code>)
|
||||||
|
* @return the cache key (never <code>null</code>)
|
||||||
|
*/
|
||||||
|
protected Object getCacheKey(Method method, Class<?> targetClass) {
|
||||||
|
return new DefaultCacheKey(method, targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
|
||||||
|
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
|
||||||
|
* @see #getTransactionAttribute
|
||||||
|
*/
|
||||||
|
private CacheDefinition computeCacheOperationDefinition(Method method, Class<?> targetClass) {
|
||||||
|
// Don't allow no-public methods as required.
|
||||||
|
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The method may be on an interface, but we need attributes from the target class.
|
||||||
|
// If the target class is null, the method will be unchanged.
|
||||||
|
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||||
|
// If we are dealing with method with generic parameters, find the original method.
|
||||||
|
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||||
|
|
||||||
|
// First try is the method in the target class.
|
||||||
|
CacheDefinition opDef = findCacheOperation(specificMethod);
|
||||||
|
if (opDef != null) {
|
||||||
|
return opDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second try is the caching operation on the target class.
|
||||||
|
opDef = findCacheDefinition(specificMethod.getDeclaringClass());
|
||||||
|
if (opDef != null) {
|
||||||
|
return opDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (specificMethod != method) {
|
||||||
|
// Fall back is to look at the original method.
|
||||||
|
opDef = findCacheOperation(method);
|
||||||
|
if (opDef != null) {
|
||||||
|
return opDef;
|
||||||
|
}
|
||||||
|
// Last fall back is the class of the original method.
|
||||||
|
return findCacheDefinition(method.getDeclaringClass());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses need to implement this to return the caching attribute
|
||||||
|
* for the given method, if any.
|
||||||
|
* @param method the method to retrieve the attribute for
|
||||||
|
* @return all caching attribute associated with this method
|
||||||
|
* (or <code>null</code> if none)
|
||||||
|
*/
|
||||||
|
protected abstract CacheDefinition findCacheOperation(Method method);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses need to implement this to return the caching attribute
|
||||||
|
* for the given class, if any.
|
||||||
|
* @param clazz the class to retrieve the attribute for
|
||||||
|
* @return all caching attribute associated with this class
|
||||||
|
* (or <code>null</code> if none)
|
||||||
|
*/
|
||||||
|
protected abstract CacheDefinition findCacheDefinition(Class<?> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should only public methods be allowed to have caching semantics?
|
||||||
|
* <p>The default implementation returns <code>false</code>.
|
||||||
|
*/
|
||||||
|
protected boolean allowPublicMethodsOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default cache key for the CacheOperationDefinition 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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 org.springframework.aop.ClassFilter;
|
||||||
|
import org.springframework.aop.Pointcut;
|
||||||
|
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advisor driven by a {@link CacheDefinitionSource}, used to include a
|
||||||
|
* transaction advice bean for methods that are transactional.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class BeanFactoryCacheDefinitionSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
|
||||||
|
|
||||||
|
private CacheDefinitionSource cacheDefinitionSource;
|
||||||
|
|
||||||
|
private final CacheDefinitionSourcePointcut pointcut = new CacheDefinitionSourcePointcut() {
|
||||||
|
@Override
|
||||||
|
protected CacheDefinitionSource getCacheDefinitionSource() {
|
||||||
|
return cacheDefinitionSource;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the cache operation attribute source which is used to find cache
|
||||||
|
* attributes. This should usually be identical to the source reference
|
||||||
|
* set on the cache interceptor itself.
|
||||||
|
* @see CacheInterceptor#setCacheAttributeSource
|
||||||
|
*/
|
||||||
|
public void setCacheDefinitionSource(CacheDefinitionSource cacheDefinitionSource) {
|
||||||
|
this.cacheDefinitionSource = cacheDefinitionSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link ClassFilter} to use for this pointcut.
|
||||||
|
* Default is {@link ClassFilter#TRUE}.
|
||||||
|
*/
|
||||||
|
public void setClassFilter(ClassFilter classFilter) {
|
||||||
|
this.pointcut.setClassFilter(classFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pointcut getPointcut() {
|
||||||
|
return this.pointcut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.io.Serializable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.aop.framework.AopProxyUtils;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.KeyGenerator;
|
||||||
|
import org.springframework.cache.support.DefaultKeyGenerator;
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for caching aspects, such as the {@link CacheInterceptor}
|
||||||
|
* or an AspectJ aspect.
|
||||||
|
*
|
||||||
|
* <p>This enables the underlying Spring caching infrastructure to be used easily
|
||||||
|
* to implement an aspect for any aspect system.
|
||||||
|
*
|
||||||
|
* <p>Subclasses are responsible for calling methods in this class in the correct order.
|
||||||
|
*
|
||||||
|
* <p>If no caching name has been specified in the <code>CacheOperationDefinition</code>,
|
||||||
|
* the exposed name will be the <code>fully-qualified class name + "." + method name</code>
|
||||||
|
* (by default).
|
||||||
|
*
|
||||||
|
* <p>Uses the <b>Strategy</b> design pattern. A <code>CacheManager</code>
|
||||||
|
* implementation will perform the actual transaction management, and a
|
||||||
|
* <code>CacheDefinitionSource</code> is used for determining caching operation definitions.
|
||||||
|
*
|
||||||
|
* <p>A cache aspect is serializable if its <code>CacheManager</code>
|
||||||
|
* and <code>CacheDefinitionSource</code> are serializable.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public abstract class CacheAspectSupport implements InitializingBean {
|
||||||
|
|
||||||
|
private static class EmptyHolder implements Serializable {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Object NULL_RETURN = new EmptyHolder();
|
||||||
|
|
||||||
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
private CacheManager cacheManager;
|
||||||
|
|
||||||
|
private CacheDefinitionSource cacheDefinitionSource;
|
||||||
|
|
||||||
|
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
|
||||||
|
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
if (this.cacheManager == null) {
|
||||||
|
throw new IllegalStateException("Setting the property 'cacheManager' is required");
|
||||||
|
}
|
||||||
|
if (this.cacheDefinitionSource == null) {
|
||||||
|
throw new IllegalStateException("Either 'cacheDefinitionSource' or 'cacheDefinitionSources' is required: "
|
||||||
|
+ "If there are no cacheable methods, then don't use a cache aspect.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to return a String representation of this Method
|
||||||
|
* for use in logging. Can be overridden in subclasses to provide a
|
||||||
|
* different identifier for the given method.
|
||||||
|
* @param method the method we're interested in
|
||||||
|
* @param targetClass class the method is on
|
||||||
|
* @return log message identifying this method
|
||||||
|
* @see org.springframework.util.ClassUtils#getQualifiedMethodName
|
||||||
|
*/
|
||||||
|
protected String methodIdentification(Method method, Class<?> targetClass) {
|
||||||
|
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||||
|
return ClassUtils.getQualifiedMethodName(specificMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheManager getCacheManager() {
|
||||||
|
return cacheManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCacheManager(CacheManager cacheManager) {
|
||||||
|
this.cacheManager = cacheManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheDefinitionSource getCacheDefinitionSource() {
|
||||||
|
return cacheDefinitionSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set multiple cache definition sources which are used to find the cache
|
||||||
|
* attributes. Will build a CompositeCachingDefinitionSource for the given sources.
|
||||||
|
*/
|
||||||
|
public void setCacheDefinitionSources(CacheDefinitionSource... cacheDefinitionSources) {
|
||||||
|
Assert.notEmpty(cacheDefinitionSources);
|
||||||
|
this.cacheDefinitionSource = (cacheDefinitionSources.length > 1 ? new CompositeCacheDefinitionSource(
|
||||||
|
cacheDefinitionSources) : cacheDefinitionSources[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <K, V> Cache<K, V> getCache(CacheDefinition definition) {
|
||||||
|
// TODO: add support for multiple caches
|
||||||
|
// TODO: add behaviour for the default cache
|
||||||
|
String name = definition.getCacheName();
|
||||||
|
if (!StringUtils.hasText(name)) {
|
||||||
|
name = cacheManager.getCacheNames().iterator().next();
|
||||||
|
}
|
||||||
|
return cacheManager.getCache(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CacheOperationContext getOperationContext(CacheDefinition definition, Method method, Object[] args, Class<?> targetClass) {
|
||||||
|
return new CacheOperationContext(definition, method, args, targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object execute(Callable<Object> invocation, Object target, Method method, Object[] args) throws Exception {
|
||||||
|
// get backing class
|
||||||
|
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
|
||||||
|
|
||||||
|
if (targetClass == null && target != null) {
|
||||||
|
targetClass = target.getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
final CacheDefinition cacheDef = getCacheDefinitionSource().getCacheDefinition(method,
|
||||||
|
targetClass);
|
||||||
|
|
||||||
|
Object retVal = null;
|
||||||
|
|
||||||
|
// analyze caching information
|
||||||
|
if (cacheDef != null) {
|
||||||
|
CacheOperationContext context = getOperationContext(cacheDef, method, args, targetClass);
|
||||||
|
Cache<Object, Object> cache = (Cache<Object, Object>) context.getCache();
|
||||||
|
|
||||||
|
if (context.hasConditionPassed()) {
|
||||||
|
// check operation
|
||||||
|
if (cacheDef instanceof CacheUpdateDefinition) {
|
||||||
|
// check cache first
|
||||||
|
Object key = context.generateKey();
|
||||||
|
retVal = cache.get(key);
|
||||||
|
if (retVal == null) {
|
||||||
|
retVal = invocation.call();
|
||||||
|
cache.put(key, (retVal == null ? NULL_RETURN : retVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheDef instanceof CacheInvalidateDefinition) {
|
||||||
|
CacheInvalidateDefinition invalidateDef = (CacheInvalidateDefinition) cacheDef;
|
||||||
|
retVal = invocation.call();
|
||||||
|
|
||||||
|
// flush the cache (ignore arguments)
|
||||||
|
if (invalidateDef.isCacheWide()) {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// check key
|
||||||
|
Object key = context.generateKey();
|
||||||
|
cache.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return invocation.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class CacheOperationContext {
|
||||||
|
|
||||||
|
private CacheDefinition definition;
|
||||||
|
private final Cache<?, ?> cache;
|
||||||
|
private final Method method;
|
||||||
|
private final Object[] args;
|
||||||
|
|
||||||
|
// context passed around to avoid multiple creations
|
||||||
|
private final EvaluationContext evalContext;
|
||||||
|
|
||||||
|
private final KeyGenerator keyGenerator = new DefaultKeyGenerator();
|
||||||
|
|
||||||
|
public CacheOperationContext(CacheDefinition operationDefinition, Method method, Object[] args,
|
||||||
|
Class<?> targetClass) {
|
||||||
|
this.definition = operationDefinition;
|
||||||
|
this.cache = CacheAspectSupport.this.getCache(definition);
|
||||||
|
this.method = method;
|
||||||
|
this.args = args;
|
||||||
|
|
||||||
|
this.evalContext = evaluator.createEvaluationContext(cache, method, args, targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the definition condition.
|
||||||
|
*
|
||||||
|
* @param definition
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean hasConditionPassed() {
|
||||||
|
if (StringUtils.hasText(definition.getCondition())) {
|
||||||
|
return evaluator.condition(definition.getCondition(), method, evalContext);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the key for the given caching definition.
|
||||||
|
*
|
||||||
|
* @param definition
|
||||||
|
* @param method method being invoked
|
||||||
|
* @param objects arguments passed during the method invocation
|
||||||
|
* @return generated key (null if none can be generated)
|
||||||
|
*/
|
||||||
|
protected Object generateKey() {
|
||||||
|
if (StringUtils.hasText(definition.getKey())) {
|
||||||
|
return evaluator.key(definition.getKey(), method, evalContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyGenerator.extract(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Cache<?, ?> getCache() {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describing Spring-compliant caching operation.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheDefinition {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this operation. Can be <tt>null</tt>.
|
||||||
|
* In case of Spring's declarative caching, the exposed name will be:
|
||||||
|
* <tt>fully qualified class name.method name</tt>.
|
||||||
|
*
|
||||||
|
* @return the operation name
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the cache against which this operation is performed.
|
||||||
|
*
|
||||||
|
* @return name of the cache on which the operation is performed.
|
||||||
|
*/
|
||||||
|
String getCacheName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SpEL expression conditioning the operation.
|
||||||
|
*
|
||||||
|
* @return operation condition (as SpEL expression).
|
||||||
|
*/
|
||||||
|
String getCondition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SpEL expression identifying the cache key.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getKey();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used by CacheInterceptor. Implementations know
|
||||||
|
* how to source cache operation attributes, whether from configuration,
|
||||||
|
* metadata attributes at source level, or anywhere else.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheDefinitionSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the cache operation definition for this method.
|
||||||
|
* Return null if the method is not cacheable.
|
||||||
|
* @param method method
|
||||||
|
* @param targetClass target class. May be <code>null</code>, in which
|
||||||
|
* case the declaring class of the method must be used.
|
||||||
|
* @return {@link CacheDefinition} the matching cache operation definition,
|
||||||
|
* or <code>null</code> if none found
|
||||||
|
*/
|
||||||
|
CacheDefinition getCacheDefinition(Method method, Class<?> targetClass);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.io.Serializable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.springframework.aop.support.StaticMethodMatcherPointcut;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class that implements a Pointcut that matches if the underlying
|
||||||
|
* {@link CacheDefinitionSource} has an attribute for a given method.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
abstract class CacheDefinitionSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||||
|
|
||||||
|
public boolean matches(Method method, Class<?> targetClass) {
|
||||||
|
CacheDefinitionSource cas = getCacheDefinitionSource();
|
||||||
|
return (cas == null || cas.getCacheDefinition(method, targetClass) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(other instanceof CacheDefinitionSourcePointcut)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CacheDefinitionSourcePointcut otherPc = (CacheDefinitionSourcePointcut) other;
|
||||||
|
return ObjectUtils.nullSafeEquals(getCacheDefinitionSource(),
|
||||||
|
otherPc.getCacheDefinitionSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return CacheDefinitionSourcePointcut.class.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getName() + ": " + getCacheDefinitionSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the underlying CacheOperationDefinitionSource (may be <code>null</code>).
|
||||||
|
* To be implemented by subclasses.
|
||||||
|
*/
|
||||||
|
protected abstract CacheDefinitionSource getCacheDefinitionSource();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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 org.springframework.cache.Cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describing the root object used during the expression evaluation.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
interface CacheExpressionRootObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the method being cached.
|
||||||
|
*
|
||||||
|
* @return name of the cached method.
|
||||||
|
*/
|
||||||
|
String getMethodName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cache against which the method is executed.
|
||||||
|
*
|
||||||
|
* @return current cache
|
||||||
|
*/
|
||||||
|
Cache getCache();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.io.Serializable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AOP Alliance MethodInterceptor for declarative cache
|
||||||
|
* management using the common Spring caching infrastructure
|
||||||
|
* ({@link org.springframework.cache.Cache}).
|
||||||
|
*
|
||||||
|
* <p>Derives from the {@link CacheAspectSupport} class which
|
||||||
|
* contains the integration with Spring's underlying caching API.
|
||||||
|
* CacheInterceptor simply calls the relevant superclass methods
|
||||||
|
* in the correct order.
|
||||||
|
*
|
||||||
|
* <p>CacheInterceptors are thread-safe.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||||
|
Method method = invocation.getMethod();
|
||||||
|
|
||||||
|
Callable<Object> aopAllianceInvocation = new Callable<Object>() {
|
||||||
|
|
||||||
|
public Object call() throws Exception {
|
||||||
|
try {
|
||||||
|
return invocation.proceed();
|
||||||
|
} catch (Throwable th) {
|
||||||
|
if (th instanceof Exception) {
|
||||||
|
throw (Exception) th;
|
||||||
|
}
|
||||||
|
throw (Error) th;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return execute(aopAllianceInvocation, invocation.getThis(), method, invocation.getArguments());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describing a Spring cache invalidation.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheInvalidateDefinition extends CacheDefinition {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the operation affects the entire cache or not.
|
||||||
|
*
|
||||||
|
* @return whether the operation is cache wide or not.
|
||||||
|
*/
|
||||||
|
boolean isCacheWide();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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 org.springframework.aop.Pointcut;
|
||||||
|
import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy factory bean for simplified declarative caching handling.
|
||||||
|
* This is a convenient alternative to a standard AOP
|
||||||
|
* {@link org.springframework.aop.framework.ProxyFactoryBean}
|
||||||
|
* with a separate {@link CachingInterceptor} definition.
|
||||||
|
*
|
||||||
|
* <p>This class is intended to cover the <i>typical</i> case of declarative
|
||||||
|
* transaction demarcation: namely, wrapping a singleton target object with a
|
||||||
|
* caching proxy, proxying all the interfaces that the target implements.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
* @see org.springframework.aop.framework.ProxyFactoryBean
|
||||||
|
* @see CachingInterceptor
|
||||||
|
*/
|
||||||
|
public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
|
||||||
|
|
||||||
|
private final CacheInterceptor cachingInterceptor = new CacheInterceptor();
|
||||||
|
private Pointcut pointcut;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object createMainInterceptor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the caching attribute source which is used to find the cache operation
|
||||||
|
* definition.
|
||||||
|
*
|
||||||
|
* @param cacheDefinitionSources cache definition sources
|
||||||
|
*/
|
||||||
|
public void setCacheDefinitionSources(CacheDefinitionSource... cacheDefinitionSources) {
|
||||||
|
this.cachingInterceptor.setCacheDefinitionSources(cacheDefinitionSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describing a Spring cache update.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheUpdateDefinition extends CacheDefinition {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SpEL expression identifying the cache key.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getKey();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.io.Serializable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composite {@link CacheDefinitionSource} implementation that iterates
|
||||||
|
* over a given array of {@link CacheDefinitionSource} instances.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class CompositeCacheDefinitionSource implements CacheDefinitionSource, Serializable {
|
||||||
|
|
||||||
|
private final CacheDefinitionSource[] cacheDefinitionSources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new CompositeCachingDefinitionSource for the given sources.
|
||||||
|
* @param cacheDefinitionSourcess the CacheDefinitionSource instances to combine
|
||||||
|
*/
|
||||||
|
public CompositeCacheDefinitionSource(CacheDefinitionSource[] cacheDefinitionSources) {
|
||||||
|
Assert.notNull(cacheDefinitionSources, "cacheDefinitionSource array must not be null");
|
||||||
|
this.cacheDefinitionSources = cacheDefinitionSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the CacheDefinitionSource instances that this
|
||||||
|
* CompositeCachingDefinitionSource combines.
|
||||||
|
*/
|
||||||
|
public final CacheDefinitionSource[] getCacheDefinitionSources() {
|
||||||
|
return this.cacheDefinitionSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public CacheDefinition getCacheDefinition(Method method, Class<?> targetClass) {
|
||||||
|
for (CacheDefinitionSource source : cacheDefinitionSources) {
|
||||||
|
CacheDefinition definition = source.getCacheDefinition(method, targetClass);
|
||||||
|
if (definition != null) {
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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 org.springframework.cache.Cache;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of expression root object.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class DefaultCacheExpressionRootObject implements CacheExpressionRootObject {
|
||||||
|
|
||||||
|
private final String methodName;
|
||||||
|
private final Cache cache;
|
||||||
|
|
||||||
|
public DefaultCacheExpressionRootObject(Cache cache, String methodName) {
|
||||||
|
Assert.hasText(methodName, "method name is required");
|
||||||
|
this.methodName = methodName;
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMethodName() {
|
||||||
|
return methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cache getCache() {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the {@link CacheInvalidateDefinition} interface.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class DefaultCacheInvalidateDefinition extends AbstractCacheDefinition implements
|
||||||
|
CacheInvalidateDefinition {
|
||||||
|
|
||||||
|
private boolean cacheWide = false;
|
||||||
|
|
||||||
|
public boolean isCacheWide() {
|
||||||
|
return cacheWide;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCacheWide(boolean cacheWide) {
|
||||||
|
this.cacheWide = cacheWide;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StringBuilder getDefinitionDescription() {
|
||||||
|
StringBuilder sb = super.getDefinitionDescription();
|
||||||
|
sb.append(",");
|
||||||
|
sb.append(cacheWide);
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the {@link CacheUpdateDefinition} interface.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class DefaultCacheUpdateDefinition extends AbstractCacheDefinition implements CacheUpdateDefinition {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class handling the SpEL expression parsing.
|
||||||
|
* Meant to be used as a reusable, thread-safe component.
|
||||||
|
*
|
||||||
|
* Performs internal caching for performance reasons.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
class ExpressionEvaluator {
|
||||||
|
|
||||||
|
private SpelExpressionParser parser = new SpelExpressionParser();
|
||||||
|
// shared param discoverer since it caches data internally
|
||||||
|
private ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||||
|
|
||||||
|
private Map<Method, Expression> conditionCache = new ConcurrentHashMap<Method, Expression>();
|
||||||
|
private Map<Method, Expression> keyCache = new ConcurrentHashMap<Method, Expression>();
|
||||||
|
private Map<Method, Method> targetMethodCache = new ConcurrentHashMap<Method, Method>();
|
||||||
|
|
||||||
|
EvaluationContext createEvaluationContext(Cache<?, ?> cache, Method method, Object[] args, Class<?> targetClass) {
|
||||||
|
DefaultCacheExpressionRootObject rootObject = new DefaultCacheExpressionRootObject(cache, method.getName());
|
||||||
|
StandardEvaluationContext evaluationContext = new LazyParamAwareEvaluationContext(rootObject,
|
||||||
|
paramNameDiscoverer, method, args, targetClass, targetMethodCache);
|
||||||
|
|
||||||
|
return evaluationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean condition(String conditionExpression, Method method, EvaluationContext evalContext) {
|
||||||
|
Expression condExp = conditionCache.get(conditionExpression);
|
||||||
|
if (condExp == null) {
|
||||||
|
condExp = parser.parseExpression(conditionExpression);
|
||||||
|
conditionCache.put(method, condExp);
|
||||||
|
}
|
||||||
|
return condExp.getValue(evalContext, boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object key(String keyExpression, Method method, EvaluationContext evalContext) {
|
||||||
|
Expression keyExp = keyCache.get(keyExpression);
|
||||||
|
if (keyExp == null) {
|
||||||
|
keyExp = parser.parseExpression(keyExpression);
|
||||||
|
keyCache.put(method, keyExp);
|
||||||
|
}
|
||||||
|
return keyExp.getValue(evalContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.aop.support.AopUtils;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluation context class that adds a method parameters as SpEL variables,
|
||||||
|
* in a lazy manner. The lazy nature eliminates unneeded parsing of classes
|
||||||
|
* byte code for parameter discovery.
|
||||||
|
*
|
||||||
|
* To limit the creation of objects, an ugly constructor is used (rather then a
|
||||||
|
* dedicated 'closure'-like class for deferred execution).
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
|
||||||
|
|
||||||
|
private final ParameterNameDiscoverer paramDiscoverer;
|
||||||
|
private final Method method;
|
||||||
|
private final Object[] args;
|
||||||
|
private Class<?> targetClass;
|
||||||
|
private Map<Method, Method> methodCache;
|
||||||
|
|
||||||
|
private boolean paramLoaded = false;
|
||||||
|
|
||||||
|
LazyParamAwareEvaluationContext(Object rootObject, ParameterNameDiscoverer paramDiscoverer, Method method,
|
||||||
|
Object[] args, Class<?> targetClass, Map<Method, Method> methodCache) {
|
||||||
|
this.paramDiscoverer = paramDiscoverer;
|
||||||
|
this.method = method;
|
||||||
|
this.args = args;
|
||||||
|
this.targetClass = targetClass;
|
||||||
|
this.methodCache = methodCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the param information only when needed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object lookupVariable(String name) {
|
||||||
|
Object variable = super.lookupVariable(name);
|
||||||
|
if (variable != null) {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!paramLoaded) {
|
||||||
|
paramLoaded = true;
|
||||||
|
loadArgsAsVariables();
|
||||||
|
variable = super.lookupVariable(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadArgsAsVariables() {
|
||||||
|
// shortcut if no args need to be loaded
|
||||||
|
if (ObjectUtils.isEmpty(args)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Method targetMethod = methodCache.get(method);
|
||||||
|
if (targetMethod == null) {
|
||||||
|
targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
||||||
|
if (targetMethod == null) {
|
||||||
|
targetMethod = method;
|
||||||
|
}
|
||||||
|
methodCache.put(method, targetMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save arguments as indexed variables
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
super.setVariable("p" + i, args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parameterNames = paramDiscoverer.getParameterNames(targetMethod);
|
||||||
|
// save parameter names (if discovered)
|
||||||
|
if (parameterNames != null) {
|
||||||
|
for (int i = 0; i < parameterNames.length; i++) {
|
||||||
|
super.setVariable(parameterNames[i], args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.support;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class implementing the common CacheManager methods. Useful for 'static' environments where the
|
||||||
|
* backing caches do not change.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
|
||||||
|
|
||||||
|
// fast lookup by name map
|
||||||
|
private final ConcurrentMap<String, Cache<?, ?>> caches = new ConcurrentHashMap<String, Cache<?, ?>>();
|
||||||
|
private Collection<String> names;
|
||||||
|
|
||||||
|
public void afterPropertiesSet() {
|
||||||
|
Collection<Cache<?, ?>> cacheSet = loadCaches();
|
||||||
|
|
||||||
|
Assert.notEmpty(cacheSet);
|
||||||
|
|
||||||
|
caches.clear();
|
||||||
|
|
||||||
|
// preserve the initial order of the cache names
|
||||||
|
Set<String> cacheNames = new LinkedHashSet<String>(cacheSet.size());
|
||||||
|
|
||||||
|
for (Cache<?, ?> cache : cacheSet) {
|
||||||
|
caches.put(cache.getName(), cache);
|
||||||
|
cacheNames.add(cache.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
names = Collections.unmodifiableSet(cacheNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the caches into the cache manager. Occurs at startup.
|
||||||
|
* The returned collection should not be null.
|
||||||
|
*
|
||||||
|
* @param caches the collection of caches handled by the manager
|
||||||
|
*/
|
||||||
|
protected abstract Collection<Cache<?, ?>> loadCaches();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the internal cache map.
|
||||||
|
*
|
||||||
|
* @return internal cache map
|
||||||
|
*/
|
||||||
|
protected final ConcurrentMap<String, Cache<?, ?>> getCacheMap() {
|
||||||
|
return caches;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <K, V> Cache<K, V> getCache(String name) {
|
||||||
|
return (Cache<K, V>) caches.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getCacheNames() {
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.support;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class delegating most of the {@link Map}-like methods
|
||||||
|
* to the underlying cache.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public abstract class AbstractDelegatingCache<K, V> implements Cache<K, V> {
|
||||||
|
|
||||||
|
private final Map<K, V> delegate;
|
||||||
|
|
||||||
|
public <D extends Map<K, V>> AbstractDelegatingCache(D delegate) {
|
||||||
|
Assert.notNull(delegate);
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
delegate.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return delegate.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V get(Object key) {
|
||||||
|
return delegate.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V put(K key, V value) {
|
||||||
|
return delegate.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V remove(Object key) {
|
||||||
|
return delegate.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.support;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composite {@link CacheManager} implementation that iterates
|
||||||
|
* over a given collection of {@link CacheManager} instances.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class CompositeCacheManager implements CacheManager {
|
||||||
|
|
||||||
|
private CacheManager[] cacheManagers;
|
||||||
|
|
||||||
|
public <K, V> Cache<K, V> getCache(String name) {
|
||||||
|
Cache<K, V> cache = null;
|
||||||
|
for (CacheManager cacheManager : cacheManagers) {
|
||||||
|
cache = cacheManager.getCache(name);
|
||||||
|
if (cache != null) {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<String> getCacheNames() {
|
||||||
|
List<String> names = new ArrayList<String>();
|
||||||
|
for (CacheManager manager : cacheManagers) {
|
||||||
|
names.addAll(manager.getCacheNames());
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableCollection(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCacheManagers(CacheManager[] cacheManagers) {
|
||||||
|
Assert.notEmpty(cacheManagers, "non-null/empty array required");
|
||||||
|
this.cacheManagers = cacheManagers.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.support;
|
||||||
|
|
||||||
|
import org.springframework.cache.KeyGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class DefaultKeyGenerator implements KeyGenerator<Object> {
|
||||||
|
|
||||||
|
public Object extract(Object... params) {
|
||||||
|
int hashCode = 17;
|
||||||
|
|
||||||
|
for (Object object : params) {
|
||||||
|
hashCode = 31 * hashCode + object.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.valueOf(hashCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.support;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple cache manager working against a given collection of caches. Useful for testing or simple
|
||||||
|
* caching declarations.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class MapCacheManager extends AbstractCacheManager {
|
||||||
|
|
||||||
|
private Collection<Cache<?, ?>> caches;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<Cache<?, ?>> loadCaches() {
|
||||||
|
return caches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCaches(Collection<Cache<?, ?>> caches) {
|
||||||
|
this.caches = caches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.support;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic {@link Entry} implementation.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class SimpleMapEntry<K, V> implements Entry<K, V> {
|
||||||
|
private K key;
|
||||||
|
private V value;
|
||||||
|
|
||||||
|
public SimpleMapEntry(K key, V value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleMapEntry(Map.Entry<K, V> e) {
|
||||||
|
this.key = e.getKey();
|
||||||
|
this.value = e.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V setValue(V value) {
|
||||||
|
V oldValue = this.value;
|
||||||
|
this.value = value;
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Map.Entry))
|
||||||
|
return false;
|
||||||
|
Map.Entry e = (Map.Entry) o;
|
||||||
|
return eq(key, e.getKey()) && eq(value, e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return ((key == null) ? 0 : key.hashCode()) ^ ((value == null) ? 0 : value.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return key + "=" + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean eq(Object o1, Object o2) {
|
||||||
|
return (o1 == null ? o2 == null : o1.equals(o2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
http\://www.springframework.org/schema/cache/spring-cache-3.1.xsd=org/springframework/cache/config/spring-cache-3.1.xsd
|
||||||
|
http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache-3.1.xsd
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Tooling related information for the cache namespace
|
||||||
|
http\://www.springframework.org/schema/cache@name=cache Namespace
|
||||||
|
http\://www.springframework.org/schema/cache@prefix=cache
|
||||||
|
http\://www.springframework.org/schema/cache@icon=org/springframework/cache/config/spring-cache.gif
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<xsd:schema xmlns="http://www.springframework.org/schema/cache"
|
||||||
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||||
|
targetNamespace="http://www.springframework.org/schema/cache"
|
||||||
|
elementFormDefault="qualified"
|
||||||
|
attributeFormDefault="unqualified">
|
||||||
|
|
||||||
|
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"/>
|
||||||
|
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.0.xsd"/>
|
||||||
|
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Defines the elements used in the Spring Framework's declarative
|
||||||
|
cache management infrastructure.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
|
||||||
|
<xsd:element name="annotation-driven">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:org.springframework.cache.annotation.AnnotationCacheOperationDefinitionSource"><![CDATA[
|
||||||
|
Indicates that cache configuration is defined by Java 5
|
||||||
|
annotations on bean classes, and that proxies are automatically
|
||||||
|
to be created for the relevant annotated beans.
|
||||||
|
|
||||||
|
The default annotations supported are Spring's @Cacheable and @CacheInvalidate.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:attribute name="cache-manager" type="xsd:string" default="cacheManager">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:org.springframework.cache.CacheManager"><![CDATA[
|
||||||
|
The bean name of the CacheManager that is to be used to retrieve the backing caches.
|
||||||
|
|
||||||
|
This attribute is not required, and only needs to be specified
|
||||||
|
explicitly if the bean name of the desired CacheManager
|
||||||
|
is not 'cacheManager'.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation kind="ref">
|
||||||
|
<tool:expected-type type="org.springframework.cache.CacheManager"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="mode" default="proxy">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Should annotated beans be proxied using Spring's AOP framework,
|
||||||
|
or should they rather be weaved with an AspectJ transaction aspect?
|
||||||
|
|
||||||
|
AspectJ weaving requires spring-aspects.jar on the classpath,
|
||||||
|
as well as load-time weaving (or compile-time weaving) enabled.
|
||||||
|
|
||||||
|
Note: The weaving-based aspect requires the @Cacheable and @CacheInvalidate
|
||||||
|
annotations to be defined on the concrete class. Annotations in interfaces
|
||||||
|
will not work in that case (they will rather only work with interface-based proxies)!
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:simpleType>
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="proxy"/>
|
||||||
|
<xsd:enumeration value="aspectj"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||||
|
Java interface-based proxies are created.
|
||||||
|
|
||||||
|
Note: Class-based proxies require the @Cacheable and @CacheInvalidate annotations
|
||||||
|
to be defined on the concrete class. Annotations in interfaces will not work
|
||||||
|
in that case (they will rather only work with interface-based proxies)!
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="order" type="xsd:int">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
|
||||||
|
Controls the ordering of the execution of the cache advisor
|
||||||
|
when multiple advice executes at a specific joinpoint.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
</xsd:schema>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 592 B |
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.vendor.AbstractNativeCacheTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class ConcurrentCacheTest extends AbstractNativeCacheTest<ConcurrentMap<Object, Object>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Cache createCache(ConcurrentMap<Object, Object> nativeCache) {
|
||||||
|
return new ConcurrentCache(nativeCache, CACHE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConcurrentMap<Object, Object> createNativeCache() throws Exception {
|
||||||
|
return new ConcurrentHashMap<Object, Object>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotSame;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract annotation test (containing several reusable methods).
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public abstract class AbstractAnnotationTest {
|
||||||
|
|
||||||
|
protected ApplicationContext ctx;
|
||||||
|
|
||||||
|
protected CacheableService cs;
|
||||||
|
|
||||||
|
protected CacheableService ccs;
|
||||||
|
|
||||||
|
protected abstract String getConfig();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
ctx = new ClassPathXmlApplicationContext(getConfig());
|
||||||
|
cs = ctx.getBean("service", CacheableService.class);
|
||||||
|
ccs = ctx.getBean("classService", CacheableService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCacheable(CacheableService service) throws Exception {
|
||||||
|
Object o1 = new Object();
|
||||||
|
Object o2 = new Object();
|
||||||
|
|
||||||
|
Object r1 = service.cache(o1);
|
||||||
|
Object r2 = service.cache(o1);
|
||||||
|
Object r3 = service.cache(o1);
|
||||||
|
|
||||||
|
assertSame(r1, r2);
|
||||||
|
assertSame(r1, r3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInvalidate(CacheableService service) throws Exception {
|
||||||
|
Object o1 = new Object();
|
||||||
|
Object o2 = new Object();
|
||||||
|
|
||||||
|
Object r1 = service.cache(o1);
|
||||||
|
Object r2 = service.cache(o1);
|
||||||
|
|
||||||
|
assertSame(r1, r2);
|
||||||
|
service.invalidate(o1);
|
||||||
|
Object r3 = service.cache(o1);
|
||||||
|
Object r4 = service.cache(o1);
|
||||||
|
assertNotSame(r1, r3);
|
||||||
|
assertSame(r3, r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConditionalExpression(CacheableService service) throws Exception {
|
||||||
|
Object r1 = service.conditional(4);
|
||||||
|
Object r2 = service.conditional(4);
|
||||||
|
|
||||||
|
assertNotSame(r1, r2);
|
||||||
|
|
||||||
|
Object r3 = service.conditional(3);
|
||||||
|
Object r4 = service.conditional(3);
|
||||||
|
|
||||||
|
assertSame(r3, r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testKeyExpression(CacheableService service) throws Exception {
|
||||||
|
Object r1 = service.key(5, 1);
|
||||||
|
Object r2 = service.key(5, 2);
|
||||||
|
|
||||||
|
assertSame(r1, r2);
|
||||||
|
|
||||||
|
Object r3 = service.key(1, 5);
|
||||||
|
Object r4 = service.key(2, 5);
|
||||||
|
|
||||||
|
assertNotSame(r3, r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCacheable() throws Exception {
|
||||||
|
testCacheable(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidate() throws Exception {
|
||||||
|
testInvalidate(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConditionalExpression() throws Exception {
|
||||||
|
testConditionalExpression(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKeyExpression() throws Exception {
|
||||||
|
testKeyExpression(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassCacheCacheable() throws Exception {
|
||||||
|
testCacheable(ccs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassCacheInvalidate() throws Exception {
|
||||||
|
testInvalidate(ccs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
@Cacheable
|
||||||
|
public class AnnotatedClassCacheableService implements CacheableService {
|
||||||
|
|
||||||
|
private AtomicLong counter = new AtomicLong();
|
||||||
|
|
||||||
|
public Object cache(Object arg1) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object conditional(int field) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict
|
||||||
|
public void invalidate(Object arg1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(key = "#p0")
|
||||||
|
public Object key(Object arg1, Object arg2) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class AnnotationNamespaceDrivenTest extends AbstractAnnotationTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getConfig() {
|
||||||
|
return "/org/springframework/cache/config/annotationDrivenCacheNamespace.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class AnnotationTest extends AbstractAnnotationTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getConfig() {
|
||||||
|
return "/org/springframework/cache/config/annotationDrivenCacheConfig.xml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic service interface.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public interface CacheableService<T> {
|
||||||
|
|
||||||
|
T cache(Object arg1);
|
||||||
|
|
||||||
|
void invalidate(Object arg1);
|
||||||
|
|
||||||
|
T conditional(int field);
|
||||||
|
|
||||||
|
T key(Object arg1, Object arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple cacheable service
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class DefaultCacheableService implements CacheableService<Long> {
|
||||||
|
|
||||||
|
private AtomicLong counter = new AtomicLong();
|
||||||
|
|
||||||
|
@Cacheable
|
||||||
|
public Long cache(Object arg1) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict
|
||||||
|
public void invalidate(Object arg1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(condition = "#classField == 3")
|
||||||
|
public Long conditional(int classField) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(key = "#p0")
|
||||||
|
public Long key(Object arg1, Object arg2) {
|
||||||
|
return counter.getAndIncrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.ehcache;
|
||||||
|
|
||||||
|
import net.sf.ehcache.Ehcache;
|
||||||
|
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
import org.springframework.cache.vendor.AbstractNativeCacheTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for EhCache cache.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public class EhCacheCacheTest extends AbstractNativeCacheTest<Ehcache> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Ehcache createNativeCache() throws Exception {
|
||||||
|
EhCacheFactoryBean fb = new EhCacheFactoryBean();
|
||||||
|
fb.setBeanName(CACHE_NAME);
|
||||||
|
fb.setCacheName(CACHE_NAME);
|
||||||
|
fb.afterPropertiesSet();
|
||||||
|
return fb.getObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Cache createCache(Ehcache nativeCache) {
|
||||||
|
return new EhCacheCache(nativeCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 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.vendor;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.cache.Cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for native cache implementations.
|
||||||
|
*
|
||||||
|
* @author Costin Leau
|
||||||
|
*/
|
||||||
|
public abstract class AbstractNativeCacheTest<T> {
|
||||||
|
|
||||||
|
private T nativeCache;
|
||||||
|
private Cache cache;
|
||||||
|
protected final static String CACHE_NAME = "testCache";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
nativeCache = createNativeCache();
|
||||||
|
cache = createCache(nativeCache);
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract T createNativeCache() throws Exception;
|
||||||
|
|
||||||
|
protected abstract Cache createCache(T nativeCache);
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCacheName() throws Exception {
|
||||||
|
assertEquals(CACHE_NAME, cache.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNativeCache() throws Exception {
|
||||||
|
assertSame(nativeCache, cache.getNativeCache());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCachePut() throws Exception {
|
||||||
|
|
||||||
|
Object key = "enescu";
|
||||||
|
Object value = "george";
|
||||||
|
|
||||||
|
assertNull(cache.get(key));
|
||||||
|
cache.put(key, value);
|
||||||
|
assertEquals(value, cache.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCacheRemove() throws Exception {
|
||||||
|
Object key = "enescu";
|
||||||
|
Object value = "george";
|
||||||
|
|
||||||
|
assertNull(cache.get(key));
|
||||||
|
cache.put(key, value);
|
||||||
|
assertEquals(value, cache.remove(key));
|
||||||
|
assertNull(cache.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCacheClear() throws Exception {
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
cache.put("enescu", "george");
|
||||||
|
assertNull(cache.get("vlaicu"));
|
||||||
|
cache.put("vlaicu", "aurel");
|
||||||
|
cache.clear();
|
||||||
|
assertNull(cache.get("vlaicu"));
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// concurrent map tests
|
||||||
|
@Test
|
||||||
|
public void testPutIfAbsent() throws Exception {
|
||||||
|
Object key = "enescu";
|
||||||
|
Object value1 = "george";
|
||||||
|
Object value2 = "geo";
|
||||||
|
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
cache.put(key, value1);
|
||||||
|
cache.putIfAbsent(key, value2);
|
||||||
|
assertEquals(value1, cache.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConcurrentRemove() throws Exception {
|
||||||
|
Object key = "enescu";
|
||||||
|
Object value1 = "george";
|
||||||
|
Object value2 = "geo";
|
||||||
|
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
cache.put(key, value1);
|
||||||
|
// no remove
|
||||||
|
cache.remove(key, value2);
|
||||||
|
assertEquals(value1, cache.get(key));
|
||||||
|
// one remove
|
||||||
|
cache.remove(key, value1);
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConcurrentReplace() throws Exception {
|
||||||
|
Object key = "enescu";
|
||||||
|
Object value1 = "george";
|
||||||
|
Object value2 = "geo";
|
||||||
|
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
cache.put(key, value1);
|
||||||
|
cache.replace(key, value2);
|
||||||
|
assertEquals(value2, cache.get(key));
|
||||||
|
cache.remove(key);
|
||||||
|
cache.replace(key, value1);
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConcurrentReplaceIfEqual() throws Exception {
|
||||||
|
Object key = "enescu";
|
||||||
|
Object value1 = "george";
|
||||||
|
Object value2 = "geo";
|
||||||
|
|
||||||
|
assertNull(cache.get("enescu"));
|
||||||
|
cache.put(key, value1);
|
||||||
|
assertEquals(value1, cache.get(key));
|
||||||
|
// no replace
|
||||||
|
cache.replace(key, value2, value1);
|
||||||
|
assertEquals(value1, cache.get(key));
|
||||||
|
cache.replace(key, value1, value2);
|
||||||
|
assertEquals(value2, cache.get(key));
|
||||||
|
cache.replace(key, value2, value1);
|
||||||
|
assertEquals(value1, cache.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||||
|
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||||
|
xmlns:p="http://www.springframework.org/schema/p"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||||
|
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
|
||||||
|
|
||||||
|
<bean id="apc" class="org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator"/>
|
||||||
|
|
||||||
|
<bean id="annotationSource" class="org.springframework.cache.annotation.AnnotationCacheDefinitionSource"/>
|
||||||
|
|
||||||
|
<aop:config>
|
||||||
|
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/>
|
||||||
|
</aop:config>
|
||||||
|
|
||||||
|
<bean id="cacheInterceptor" class="org.springframework.cache.interceptor.CacheInterceptor">
|
||||||
|
<property name="cacheManager" ref="cacheManager"/>
|
||||||
|
<property name="cacheDefinitionSources" ref="annotationSource"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="advisor" class="org.springframework.cache.interceptor.BeanFactoryCacheDefinitionSourceAdvisor">
|
||||||
|
<property name="cacheDefinitionSource" ref="annotationSource"/>
|
||||||
|
<property name="adviceBeanName" value="cacheInterceptor"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
|
||||||
|
<bean id="cacheManager" class="org.springframework.cache.support.MapCacheManager">
|
||||||
|
<property name="caches">
|
||||||
|
<set>
|
||||||
|
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/>
|
||||||
|
</set>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
|
||||||
|
|
||||||
|
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>
|
||||||
|
|
||||||
|
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/>
|
||||||
|
|
||||||
|
</beans>
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||||
|
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||||
|
xmlns:p="http://www.springframework.org/schema/p"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||||
|
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
|
||||||
|
|
||||||
|
<cache:annotation-driven proxy-target-class="false" order="0"/>
|
||||||
|
|
||||||
|
<aop:config>
|
||||||
|
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/>
|
||||||
|
</aop:config>
|
||||||
|
|
||||||
|
<bean id="cacheManager" class="org.springframework.cache.support.MapCacheManager">
|
||||||
|
<property name="caches">
|
||||||
|
<set>
|
||||||
|
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/>
|
||||||
|
</set>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
|
||||||
|
|
||||||
|
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>
|
||||||
|
|
||||||
|
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/>
|
||||||
|
|
||||||
|
</beans>
|
||||||
Loading…
Reference in New Issue