Skip to content

Commit 05072e1

Browse files
author
Phillip Webb
committed
Expand var-args before passing to KeyGenerator
Update `CacheAspectSupport` to expand any var-arg parameters before calling `KeyGenerator` implementations. Prior to this commit var-args would be passed to `KeyGenerator` implementations as a nested array, often causing the same key to be generated regardless of the arguments. Issue: SPR-10870
1 parent 2337e76 commit 05072e1

File tree

10 files changed

+72
-5
lines changed

10 files changed

+72
-5
lines changed

spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ public Object key(Object arg1, Object arg2) {
8787
return counter.getAndIncrement();
8888
}
8989

90+
@Override
91+
@Cacheable(value = "default")
92+
public Object varArgsKey(Object... args) {
93+
return counter.getAndIncrement();
94+
}
95+
9096
@Override
9197
@Cacheable(value = "default", key = "#root.methodName + #root.caches[0].name")
9298
public Object name(Object arg1) {

spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public interface CacheableService<T> {
4444

4545
T key(Object arg1, Object arg2);
4646

47+
T varArgsKey(Object... args);
48+
4749
T name(Object arg1);
4850

4951
T nullValue(Object arg1);

spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ public Long key(Object arg1, Object arg2) {
9191
return counter.getAndIncrement();
9292
}
9393

94+
@Override
95+
@Cacheable(value = "default")
96+
public Long varArgsKey(Object... args) {
97+
return counter.getAndIncrement();
98+
}
99+
94100
@Override
95101
@Cacheable(value = "default", key = "#root.methodName")
96102
public Long name(Object arg1) {

spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
import org.apache.commons.logging.Log;
2727
import org.apache.commons.logging.LogFactory;
28-
2928
import org.springframework.aop.framework.AopProxyUtils;
3029
import org.springframework.beans.factory.InitializingBean;
3130
import org.springframework.cache.Cache;
@@ -349,15 +348,27 @@ protected class CacheOperationContext {
349348

350349
private final Collection<Cache> caches;
351350

352-
public CacheOperationContext(CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
351+
public CacheOperationContext(CacheOperation operation, Method method,
352+
Object[] args, Object target, Class<?> targetClass) {
353353
this.operation = operation;
354354
this.method = method;
355-
this.args = args;
355+
this.args = extractArgs(method, args);
356356
this.target = target;
357357
this.targetClass = targetClass;
358358
this.caches = CacheAspectSupport.this.getCaches(operation);
359359
}
360360

361+
private Object[] extractArgs(Method method, Object[] args) {
362+
if (!method.isVarArgs()) {
363+
return args;
364+
}
365+
Object[] varArgs = (Object[]) args[args.length - 1];
366+
Object[] combinedArgs = new Object[args.length - 1 + varArgs.length];
367+
System.arraycopy(args, 0, combinedArgs, 0, args.length - 1);
368+
System.arraycopy(varArgs, 0, combinedArgs, args.length - 1, varArgs.length);
369+
return combinedArgs;
370+
}
371+
361372
protected boolean isConditionPassing(Object result) {
362373
if (StringUtils.hasText(this.operation.getCondition())) {
363374
EvaluationContext evaluationContext = createEvaluationContext(result);

spring-context/src/main/java/org/springframework/cache/interceptor/KeyGenerator.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2011 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,10 +24,18 @@
2424
*
2525
* @author Costin Leau
2626
* @author Chris Beams
27+
* @author Phillip Webb
2728
* @since 3.1
2829
*/
2930
public interface KeyGenerator {
3031

32+
/**
33+
* Generate a key for the given method and its parameters.
34+
* @param target the target instance
35+
* @param method the method being called
36+
* @param params the method parameters (with any var-args expanded)
37+
* @return a generated key
38+
*/
3139
Object generate(Object target, Method method, Object... params);
3240

3341
}

spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2013 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -211,6 +211,19 @@ public void testKeyExpression(CacheableService<?> service) throws Exception {
211211
assertNotSame(r3, r4);
212212
}
213213

214+
public void testVarArgsKey(CacheableService<?> service) throws Exception {
215+
Object r1 = service.varArgsKey(1, 2, 3);
216+
Object r2 = service.varArgsKey(1, 2, 3);
217+
218+
assertSame(r1, r2);
219+
220+
Object r3 = service.varArgsKey(1, 2, 3);
221+
Object r4 = service.varArgsKey(1, 2);
222+
223+
assertNotSame(r3, r4);
224+
}
225+
226+
214227
public void testNullValue(CacheableService<?> service) throws Exception {
215228
Object key = new Object();
216229
assertNull(service.nullValue(key));
@@ -478,6 +491,11 @@ public void testKeyExpression() throws Exception {
478491
testKeyExpression(cs);
479492
}
480493

494+
@Test
495+
public void testVarArgsKey() throws Exception {
496+
testVarArgsKey(cs);
497+
}
498+
481499
@Test
482500
public void testClassCacheCacheable() throws Exception {
483501
testCacheable(ccs);

spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ public Object key(Object arg1, Object arg2) {
8989
return counter.getAndIncrement();
9090
}
9191

92+
@Override
93+
@Cacheable(value = "default")
94+
public Object varArgsKey(Object... args) {
95+
return counter.getAndIncrement();
96+
}
97+
9298
@Override
9399
@Cacheable(value = "default", key = "#root.methodName + #root.caches[0].name")
94100
public Object name(Object arg1) {

spring-context/src/test/java/org/springframework/cache/config/CacheableService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public interface CacheableService<T> {
4444

4545
T key(Object arg1, Object arg2);
4646

47+
T varArgsKey(Object... args);
48+
4749
T name(Object arg1);
4850

4951
T nullValue(Object arg1);

spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ public Long key(Object arg1, Object arg2) {
9191
return counter.getAndIncrement();
9292
}
9393

94+
@Override
95+
@Cacheable(value = "default")
96+
public Long varArgsKey(Object... args) {
97+
return counter.getAndIncrement();
98+
}
99+
94100
@Override
95101
@Cacheable(value = "default", key = "#root.methodName")
96102
public Long name(Object arg1) {

spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<cache:cacheable method="conditional" condition="#classField == 3"/>
1414
<cache:cacheable method="unless" unless="#result > 10"/>
1515
<cache:cacheable method="key" key="#p0"/>
16+
<cache:cacheable method="varArgsKey"/>
1617
<cache:cacheable method="nam*" key="#root.methodName"/>
1718
<cache:cacheable method="rootVars" key="#root.methodName + #root.method.name + #root.targetClass + #root.target"/>
1819
<cache:cacheable method="nullValue" cache="default"/>
@@ -52,6 +53,7 @@
5253
<cache:advice id="cacheAdviceClass" cache-manager="cacheManager" key-generator="keyGenerator">
5354
<cache:caching cache="default">
5455
<cache:cacheable method="key" key="#p0"/>
56+
<cache:cacheable method="varArgsKey"/>
5557
<cache:cacheable method="nam*" key="#root.methodName + #root.caches[0].name"/>
5658
<cache:cacheable method="rootVars" key="#root.methodName + #root.method.name + #root.targetClass + #root.target"/>
5759
<cache:cacheable method="cache"/>

0 commit comments

Comments
 (0)