+ update default key generator strategy to improve compatibility for implicit declaration on one arg method
+ updated docs


git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4066 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Costin Leau 2011-03-06 17:13:24 +00:00
parent e4261b936a
commit 1eb54b700d
8 changed files with 91 additions and 6 deletions

View File

@ -0,0 +1,38 @@
<?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="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

@ -20,7 +20,7 @@ import java.lang.reflect.Method;
/**
* Cache 'key' extractor. Used for creating a key based on the given method
* (used as context) and its parameter.
* (used as context) and its parameters.
*
* @author Costin Leau
*/

View File

@ -21,14 +21,22 @@ import java.lang.reflect.Method;
import org.springframework.cache.KeyGenerator;
/**
* Default key generator. Computes a resulting key based on the hashcode of the
* given parameters.
* Default key generator. Returns 0 if no param is given, the param itself if only one is given or a hash code computed
* from all given params hash code.
*
* @author Costin Leau
*/
public class DefaultKeyGenerator implements KeyGenerator<Object> {
public Object extract(Method method, Object... params) {
if (params.length == 1) {
return params[0];
}
if (params.length == 0) {
return 0;
}
int hashCode = 17;
for (Object object : params) {

View File

@ -77,6 +77,21 @@ public abstract class AbstractAnnotationTest {
assertSame(r3, r4);
}
public void testInvalidateWKey(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, null);
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);
@ -132,6 +147,11 @@ public abstract class AbstractAnnotationTest {
testInvalidate(cs);
}
@Test
public void testInvalidateWithKey() throws Exception {
testInvalidateWKey(cs);
}
@Test
public void testConditionalExpression() throws Exception {
testConditionalExpression(cs);
@ -152,6 +172,11 @@ public abstract class AbstractAnnotationTest {
testInvalidate(ccs);
}
@Test
public void testClassCacheInvalidateWKey() throws Exception {
testInvalidateWKey(ccs);
}
@Test
public void testNullValue() throws Exception {
testNullValue(cs);

View File

@ -42,6 +42,10 @@ public class AnnotatedClassCacheableService implements CacheableService {
public void invalidate(Object arg1) {
}
@CacheEvict(value = "default", key = "#p0")
public void invalidate(Object arg1, Object arg2) {
}
@Cacheable(value = "default", key = "#p0")
public Object key(Object arg1, Object arg2) {
return counter.getAndIncrement();

View File

@ -28,6 +28,8 @@ public interface CacheableService<T> {
void invalidate(Object arg1);
void invalidate(Object arg1, Object arg2);
T conditional(int field);
T key(Object arg1, Object arg2);

View File

@ -40,6 +40,10 @@ public class DefaultCacheableService implements CacheableService<Long> {
public void invalidate(Object arg1) {
}
@CacheEvict(value = "default", key = "#p0")
public void invalidate(Object arg1, Object arg2) {
}
@Cacheable(value = "default", condition = "#classField == 3")
public Long conditional(int classField) {
return counter.getAndIncrement();

View File

@ -86,9 +86,13 @@ public Book findBook(ISBN isbn) {...}]]></programlisting>
<title>Default Key Generation</title>
<para>Since caches are essentially key-value stores, each invocation of a cached method needs to be translated into a suitable key for cache access.
Out of the box, the caching abstraction uses a simple <literal>hash-code</literal> based <interfacename>KeyGenerator</interfacename> that computes the key based on the
hashes of all objects used for method invocation. This approach works well for objects with <emphasis>natural keys</emphasis> as long as
the <literal>hashCode()</literal> reflects that. If that is not the case then
Out of the box, the caching abstraction uses a simple <interfacename>KeyGenerator</interfacename> based on the following algorithm:</para>
<itemizedlist>
<listitem>If no params are given, return 0.</listitem>
<listitem>If only one param is given, return that instance.</listitem>
<listitem>If more the one param is given, return a key computed from the hashes of all parameters.</listitem>
</itemizedlist>
This approach works well for objects with <emphasis>natural keys</emphasis> as long as the <literal>hashCode()</literal> reflects that. If that is not the case then
for distributed or persistent environments, the strategy needs to be changed as the objects hashCode is not preserved.
In fact, depending on the JVM implementation or running conditions, the same hashCode can be reused for different objects, in the same VM instance.</para>