Skip to content

Commit 14bf650

Browse files
committed
Consistent cache key implementation across transaction and cache attribute sources
Includes consistent applicability of class-level metadata to user-level methods only. Issue: SPR-14017 Issue: SPR-14095
1 parent 5619b00 commit 14bf650

File tree

5 files changed

+135
-86
lines changed

5 files changed

+135
-86
lines changed

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 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,8 +24,8 @@
2424
import org.apache.commons.logging.Log;
2525
import org.apache.commons.logging.LogFactory;
2626

27-
import org.springframework.context.expression.AnnotatedElementKey;
2827
import org.springframework.core.BridgeMethodResolver;
28+
import org.springframework.core.MethodClassKey;
2929
import org.springframework.util.ClassUtils;
3030

3131
/**
@@ -51,12 +51,12 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
5151

5252
protected final Log logger = LogFactory.getLog(getClass());
5353

54-
private final Map<Object, Object> cache = new ConcurrentHashMap<Object, Object>(1024);
54+
private final Map<MethodClassKey, Object> cache = new ConcurrentHashMap<MethodClassKey, Object>(1024);
5555

5656

5757
@Override
5858
public JCacheOperation<?> getCacheOperation(Method method, Class<?> targetClass) {
59-
Object cacheKey = new AnnotatedElementKey(method, targetClass);
59+
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
6060
Object cached = this.cache.get(cacheKey);
6161

6262
if (cached != null) {
@@ -95,7 +95,7 @@ private JCacheOperation<?> computeCacheOperation(Method method, Class<?> targetC
9595
return operation;
9696
}
9797
if (specificMethod != method) {
98-
// Fall back is to look at the original method.
98+
// Fallback is to look at the original method.
9999
operation = findCacheOperation(method, targetClass);
100100
if (operation != null) {
101101
return operation;

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

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 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.
@@ -26,27 +26,24 @@
2626
import org.apache.commons.logging.Log;
2727
import org.apache.commons.logging.LogFactory;
2828

29-
import org.springframework.context.expression.AnnotatedElementKey;
3029
import org.springframework.core.BridgeMethodResolver;
30+
import org.springframework.core.MethodClassKey;
3131
import org.springframework.util.ClassUtils;
3232

3333
/**
34-
* Abstract implementation of {@link CacheOperation} that caches
35-
* attributes for methods and implements a fallback policy: 1. specific
36-
* target method; 2. target class; 3. declaring method; 4. declaring
37-
* class/interface.
34+
* Abstract implementation of {@link CacheOperation} that caches attributes
35+
* for methods and implements a fallback policy: 1. specific target method;
36+
* 2. target class; 3. declaring method; 4. declaring class/interface.
3837
*
3938
* <p>Defaults to using the target class's caching attribute if none is
40-
* associated with the target method. Any caching attribute associated
41-
* with the target method completely overrides a class caching attribute.
42-
* If none found on the target class, the interface that the invoked
43-
* method has been called through (in case of a JDK proxy) will be
44-
* checked.
39+
* associated with the target method. Any caching attribute associated with
40+
* the target method completely overrides a class caching attribute.
41+
* If none found on the target class, the interface that the invoked method
42+
* has been called through (in case of a JDK proxy) will be checked.
4543
*
46-
* <p>This implementation caches attributes by method after they are
47-
* first used. If it is ever desirable to allow dynamic changing of
48-
* cacheable attributes (which is very unlikely), caching could be made
49-
* configurable.
44+
* <p>This implementation caches attributes by method after they are first
45+
* used. If it is ever desirable to allow dynamic changing of cacheable
46+
* attributes (which is very unlikely), caching could be made configurable.
5047
*
5148
* @author Costin Leau
5249
* @author Juergen Hoeller
@@ -69,7 +66,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
6966
protected final Log logger = LogFactory.getLog(getClass());
7067

7168
/**
72-
* Cache of CacheOperations, keyed by {@link AnnotatedElementKey}.
69+
* Cache of CacheOperations, keyed by method on a specific target class.
7370
* <p>As this base class is not marked Serializable, the cache will be recreated
7471
* after serialization - provided that the concrete subclass is Serializable.
7572
*/
@@ -117,7 +114,7 @@ public Collection<CacheOperation> getCacheOperations(Method method, Class<?> tar
117114
* @return the cache key (never {@code null})
118115
*/
119116
protected Object getCacheKey(Method method, Class<?> targetClass) {
120-
return new AnnotatedElementKey(method, targetClass);
117+
return new MethodClassKey(method, targetClass);
121118
}
122119

123120
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
@@ -140,19 +137,23 @@ private Collection<CacheOperation> computeCacheOperations(Method method, Class<?
140137

141138
// Second try is the caching operation on the target class.
142139
opDef = findCacheOperations(specificMethod.getDeclaringClass());
143-
if (opDef != null) {
140+
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
144141
return opDef;
145142
}
146143

147144
if (specificMethod != method) {
148-
// Fall back is to look at the original method.
145+
// Fallback is to look at the original method.
149146
opDef = findCacheOperations(method);
150147
if (opDef != null) {
151148
return opDef;
152149
}
153-
// Last fall back is the class of the original method.
154-
return findCacheOperations(method.getDeclaringClass());
150+
// Last fallback is the class of the original method.
151+
opDef = findCacheOperations(method.getDeclaringClass());
152+
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
153+
return opDef;
154+
}
155155
}
156+
156157
return null;
157158
}
158159

spring-context/src/main/java/org/springframework/context/expression/CachedExpressionEvaluator.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ private ExpressionKey createKey(AnnotatedElementKey elementKey, String expressio
8484
}
8585

8686

87-
protected static class ExpressionKey {
87+
protected static class ExpressionKey implements Comparable<ExpressionKey> {
8888

89-
private final AnnotatedElementKey key;
89+
private final AnnotatedElementKey element;
9090

9191
private final String expression;
9292

93-
protected ExpressionKey(AnnotatedElementKey key, String expression) {
94-
this.key = key;
93+
protected ExpressionKey(AnnotatedElementKey element, String expression) {
94+
this.element = element;
9595
this.expression = expression;
9696
}
9797

@@ -104,13 +104,27 @@ public boolean equals(Object other) {
104104
return false;
105105
}
106106
ExpressionKey otherKey = (ExpressionKey) other;
107-
return (this.key.equals(otherKey.key) &&
107+
return (this.element.equals(otherKey.element) &&
108108
ObjectUtils.nullSafeEquals(this.expression, otherKey.expression));
109109
}
110110

111111
@Override
112112
public int hashCode() {
113-
return this.key.hashCode() + (this.expression != null ? this.expression.hashCode() * 29 : 0);
113+
return this.element.hashCode() + (this.expression != null ? this.expression.hashCode() * 29 : 0);
114+
}
115+
116+
@Override
117+
public String toString() {
118+
return this.element + (this.expression != null ? " with expression \"" + this.expression : "\"");
119+
}
120+
121+
@Override
122+
public int compareTo(ExpressionKey other) {
123+
int result = this.element.toString().compareTo(other.element.toString());
124+
if (result == 0 && this.expression != null) {
125+
result = this.expression.compareTo(other.expression);
126+
}
127+
return result;
114128
}
115129
}
116130

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2002-2016 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.core;
18+
19+
import java.lang.reflect.Method;
20+
21+
import org.springframework.util.ObjectUtils;
22+
23+
/**
24+
* A common key class for a method against a specific target class,
25+
* including {@link #toString()} representation and {@link Comparable}
26+
* support (as suggested for custom {@code HashMap} keys as of Java 8).
27+
*
28+
* @author Juergen Hoeller
29+
* @since 4.3
30+
*/
31+
public final class MethodClassKey implements Comparable<MethodClassKey> {
32+
33+
private final Method method;
34+
35+
private final Class<?> targetClass;
36+
37+
38+
/**
39+
* Create a key object for the given method and target class.
40+
* @param method the method to wrap (must not be {@code null})
41+
* @param targetClass the target class that the method will be invoked
42+
* on (may be {@code null} if identical to the declaring class)
43+
*/
44+
public MethodClassKey(Method method, Class<?> targetClass) {
45+
this.method = method;
46+
this.targetClass = targetClass;
47+
}
48+
49+
50+
@Override
51+
public boolean equals(Object other) {
52+
if (this == other) {
53+
return true;
54+
}
55+
if (!(other instanceof MethodClassKey)) {
56+
return false;
57+
}
58+
MethodClassKey otherKey = (MethodClassKey) other;
59+
return (this.method.equals(otherKey.method) &&
60+
ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
return this.method.hashCode() + (this.targetClass != null ? this.targetClass.hashCode() * 29 : 0);
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return this.method + (this.targetClass != null ? " on " + this.targetClass : "");
71+
}
72+
73+
@Override
74+
public int compareTo(MethodClassKey other) {
75+
int result = this.method.getName().compareTo(other.method.getName());
76+
if (result == 0) {
77+
result = this.method.toString().compareTo(other.method.toString());
78+
if (result == 0 && this.targetClass != null) {
79+
result = this.targetClass.getName().compareTo(other.targetClass.getName());
80+
}
81+
}
82+
return result;
83+
}
84+
85+
}

spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
import org.apache.commons.logging.LogFactory;
2626

2727
import org.springframework.core.BridgeMethodResolver;
28+
import org.springframework.core.MethodClassKey;
2829
import org.springframework.util.ClassUtils;
29-
import org.springframework.util.ObjectUtils;
3030

3131
/**
3232
* Abstract implementation of {@link TransactionAttributeSource} that caches
@@ -65,7 +65,7 @@ public abstract class AbstractFallbackTransactionAttributeSource implements Tran
6565
protected final Log logger = LogFactory.getLog(getClass());
6666

6767
/**
68-
* Cache of TransactionAttributes, keyed by DefaultCacheKey (Method + target Class).
68+
* Cache of TransactionAttributes, keyed by method on a specific target class.
6969
* <p>As this base class is not marked Serializable, the cache will be recreated
7070
* after serialization - provided that the concrete subclass is Serializable.
7171
*/
@@ -123,7 +123,7 @@ public TransactionAttribute getTransactionAttribute(Method method, Class<?> targ
123123
* @return the cache key (never {@code null})
124124
*/
125125
protected Object getCacheKey(Method method, Class<?> targetClass) {
126-
return new DefaultCacheKey(method, targetClass);
126+
return new MethodClassKey(method, targetClass);
127127
}
128128

129129
/**
@@ -203,55 +203,4 @@ protected boolean allowPublicMethodsOnly() {
203203
return false;
204204
}
205205

206-
207-
/**
208-
* Default cache key for the TransactionAttribute cache.
209-
*/
210-
private static final class DefaultCacheKey implements Comparable<DefaultCacheKey> {
211-
212-
private final Method method;
213-
214-
private final Class<?> targetClass;
215-
216-
public DefaultCacheKey(Method method, Class<?> targetClass) {
217-
this.method = method;
218-
this.targetClass = targetClass;
219-
}
220-
221-
@Override
222-
public boolean equals(Object other) {
223-
if (this == other) {
224-
return true;
225-
}
226-
if (!(other instanceof DefaultCacheKey)) {
227-
return false;
228-
}
229-
DefaultCacheKey otherKey = (DefaultCacheKey) other;
230-
return (this.method.equals(otherKey.method) &&
231-
ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
232-
}
233-
234-
@Override
235-
public int hashCode() {
236-
return this.method.hashCode() + (this.targetClass != null ? this.targetClass.hashCode() * 29 : 0);
237-
}
238-
239-
@Override
240-
public String toString() {
241-
return this.method + (this.targetClass != null ? " on " + this.targetClass : "");
242-
}
243-
244-
@Override
245-
public int compareTo(DefaultCacheKey other) {
246-
int result = this.method.getName().compareTo(other.method.getName());
247-
if (result == 0) {
248-
result = this.method.toString().compareTo(other.method.toString());
249-
if (result == 0 && this.targetClass != null) {
250-
result = this.targetClass.getName().compareTo(other.targetClass.getName());
251-
}
252-
}
253-
return result;
254-
}
255-
}
256-
257206
}

0 commit comments

Comments
 (0)