Skip to content

Commit f1a18d2

Browse files
committed
SimpleKeyGenerator to replace DefaultKeyGenerator
Introduce new SimpleKeyGenerator class to supersede DefaultKeyGenerator. Unlike DefaultKeyGenerator, no collisions will occur with the keys generated by SimpleKeyGenerator as the full parameter values are considered within the SimpleKey.equals(...) method. The SimpleKeyGenerator is now the default class used when no explicit generator is configured. Issue: SPR-10237
1 parent c720d82 commit f1a18d2

File tree

6 files changed

+223
-4
lines changed

6 files changed

+223
-4
lines changed

spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@
120120
* customizing the strategy for cache key generation, per Spring's {@link
121121
* org.springframework.cache.interceptor.KeyGenerator KeyGenerator} SPI. Normally,
122122
* {@code @EnableCaching} will configure Spring's
123-
* {@link org.springframework.cache.interceptor.DefaultKeyGenerator DefaultKeyGenerator}
123+
* {@link org.springframework.cache.interceptor.SimpleKeyGenerator SimpleKeyGenerator}
124124
* for this purpose, but when implementing {@code CachingConfigurer}, a key generator
125-
* must be provided explicitly. Return {@code new DefaultKeyGenerator()} from this method
125+
* must be provided explicitly. Return {@code new SimpleKeyGenerator()} from this method
126126
* if no customization is necessary. See {@link CachingConfigurer} Javadoc for further
127127
* details.
128128
*

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public interface Invoker {
7373

7474
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
7575

76-
private KeyGenerator keyGenerator = new DefaultKeyGenerator();
76+
private KeyGenerator keyGenerator = new SimpleKeyGenerator();
7777

7878
private boolean initialized = false;
7979

@@ -116,7 +116,7 @@ public CacheOperationSource getCacheOperationSource() {
116116

117117
/**
118118
* Set the KeyGenerator for this cache aspect.
119-
* Default is {@link DefaultKeyGenerator}.
119+
* Default is {@link SimpleKeyGenerator}.
120120
*/
121121
public void setKeyGenerator(KeyGenerator keyGenerator) {
122122
this.keyGenerator = keyGenerator;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,17 @@
2727
* Uses the constant value {@value #NULL_PARAM_KEY} for any
2828
* {@code null} parameters given.
2929
*
30+
* <p>NOTE: As this implementation returns only a hash of the parameters
31+
* it is possible for key collisions to occur. Since Spring 4.0 the
32+
* {@link SimpleKeyGenerator} is used when no explicit key generator
33+
* has been defined. This class remains for applications that do not
34+
* wish to migrate to the {@link SimpleKeyGenerator}.
35+
*
3036
* @author Costin Leau
3137
* @author Chris Beams
3238
* @since 3.1
39+
* @see SimpleKeyGenerator
40+
* @see org.springframework.cache.annotation.CachingConfigurer
3341
*/
3442
public class DefaultKeyGenerator implements KeyGenerator {
3543

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cache.interceptor;
18+
19+
import java.io.Serializable;
20+
import java.util.Arrays;
21+
22+
import org.springframework.util.Assert;
23+
import org.springframework.util.StringUtils;
24+
25+
/**
26+
* A simple key as returned from the {@link SimpleKeyGenerator}.
27+
*
28+
* @author Phillip Webb
29+
* @see SimpleKeyGenerator
30+
*/
31+
public final class SimpleKey implements Serializable {
32+
33+
private static final long serialVersionUID = 1;
34+
35+
public static final SimpleKey EMPTY = new SimpleKey(new Object[] {});
36+
37+
38+
private final Object[] params;
39+
40+
41+
/**
42+
* Create a new {@link SimpleKey} instance.
43+
* @param elements the elements of the key
44+
*/
45+
public SimpleKey(Object[] elements) {
46+
Assert.notNull(elements, "Elements must not be null");
47+
this.params = new Object[elements.length];
48+
System.arraycopy(elements, 0, this.params, 0, elements.length);
49+
}
50+
51+
52+
@Override
53+
public boolean equals(Object obj) {
54+
if (obj == this) {
55+
return true;
56+
}
57+
if (obj != null && getClass() == obj.getClass()) {
58+
return Arrays.equals(this.params, ((SimpleKey) obj).params);
59+
}
60+
return false;
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
return Arrays.hashCode(params);
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return "SimpleKey [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
71+
}
72+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cache.interceptor;
18+
19+
import java.lang.reflect.Method;
20+
21+
/**
22+
* Simple key generator. Returns the parameter itself if a single non-null value
23+
* is given, otherwise returns a {@link SimpleKey} of the parameters.
24+
*
25+
* <p>Unlike {@link DefaultKeyGenerator}, no collisions will occur with the keys
26+
* generated by this class. The returned {@link SimpleKey} object can be safely
27+
* used with a {@link org.springframework.cache.concurrent.ConcurrentMapCache},
28+
* however, might not be suitable for all {@link org.springframework.cache.Cache}
29+
* implementations.
30+
*
31+
* @author Phillip Webb
32+
* @since 4.0
33+
* @see SimpleKey
34+
* @see DefaultKeyGenerator
35+
* @see org.springframework.cache.annotation.CachingConfigurer
36+
*/
37+
public class SimpleKeyGenerator implements KeyGenerator {
38+
39+
@Override
40+
public Object generate(Object target, Method method, Object... params) {
41+
if(params.length == 0) {
42+
return SimpleKey.EMPTY;
43+
}
44+
if(params.length == 1 && params[0] != null) {
45+
return params[0];
46+
}
47+
return new SimpleKey(params);
48+
}
49+
50+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cache.interceptor;
18+
19+
import org.junit.Test;
20+
21+
import static org.hamcrest.Matchers.*;
22+
import static org.junit.Assert.*;
23+
24+
/**
25+
* Tests for {@link SimpleKeyGenerator} and {@link SimpleKey}.
26+
*
27+
* @author Phillip Webb
28+
*/
29+
public class SimpleKeyGeneratorTests {
30+
31+
private SimpleKeyGenerator generator = new SimpleKeyGenerator();
32+
33+
@Test
34+
public void noValues() throws Exception {
35+
Object k1 = generator.generate(null, null, new Object[] {});
36+
Object k2 = generator.generate(null, null, new Object[] {});
37+
Object k3 = generator.generate(null, null, new Object[] { "different" });
38+
assertThat(k1.hashCode(), equalTo(k2.hashCode()));
39+
assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
40+
assertThat(k1, equalTo(k2));
41+
assertThat(k1, not(equalTo(k3)));
42+
}
43+
44+
@Test
45+
public void singleValue() throws Exception {
46+
Object k1 = generator.generate(null, null, new Object[] { "a" });
47+
Object k2 = generator.generate(null, null, new Object[] { "a" });
48+
Object k3 = generator.generate(null, null, new Object[] { "different" });
49+
assertThat(k1.hashCode(), equalTo(k2.hashCode()));
50+
assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
51+
assertThat(k1, equalTo(k2));
52+
assertThat(k1, not(equalTo(k3)));
53+
assertThat(k1, equalTo((Object) "a"));
54+
}
55+
56+
@Test
57+
public void multipleValues() throws Exception {
58+
Object k1 = generator.generate(null, null, new Object[] { "a", 1, "b" });
59+
Object k2 = generator.generate(null, null, new Object[] { "a", 1, "b" });
60+
Object k3 = generator.generate(null, null, new Object[] { "b", 1, "a" });
61+
assertThat(k1.hashCode(), equalTo(k2.hashCode()));
62+
assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
63+
assertThat(k1, equalTo(k2));
64+
assertThat(k1, not(equalTo(k3)));
65+
}
66+
67+
@Test
68+
public void singleNullValue() throws Exception {
69+
Object k1 = generator.generate(null, null, new Object[] { null });
70+
Object k2 = generator.generate(null, null, new Object[] { null });
71+
Object k3 = generator.generate(null, null, new Object[] { "different" });
72+
assertThat(k1.hashCode(), equalTo(k2.hashCode()));
73+
assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
74+
assertThat(k1, equalTo(k2));
75+
assertThat(k1, not(equalTo(k3)));
76+
assertThat(k1, instanceOf(SimpleKey.class));
77+
}
78+
79+
@Test
80+
public void multiplNullValues() throws Exception {
81+
Object k1 = generator.generate(null, null, new Object[] { "a", null, "b", null });
82+
Object k2 = generator.generate(null, null, new Object[] { "a", null, "b", null });
83+
Object k3 = generator.generate(null, null, new Object[] { "a", null, "b" });
84+
assertThat(k1.hashCode(), equalTo(k2.hashCode()));
85+
assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
86+
assertThat(k1, equalTo(k2));
87+
assertThat(k1, not(equalTo(k3)));
88+
}
89+
}

0 commit comments

Comments
 (0)