SPR-8015
+ 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:
parent
e4261b936a
commit
1eb54b700d
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue