+ 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:
Costin Leau 2010-10-29 17:00:08 +00:00
parent 334e4eb1e7
commit 5c534c3757
62 changed files with 4225 additions and 0 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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";
}
}

View File

@ -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();
}
}

View 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);
}

View File

@ -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();
}
}

View File

@ -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>

View 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();
}

View 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();
}

View 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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 "";
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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>&lt;cache:annotation-driven/&gt;</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);
}
}
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 {
}

View File

@ -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);
}
}

View File

@ -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]);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -0,0 +1 @@
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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";
}
}

View File

@ -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";
}
}

View 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);
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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>

View File

@ -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>