Skip to content

Commit c7b324b

Browse files
committed
Move cached expression evaluation abstraction
Move MethodCacheKey and related classes to the expression package so that other parts of the framework can benefit ot it. CacheExpressionEvaluator is a base class that can be used to cache SpEL expressions based on its annotation source (i.e. method). Sub-classing that base class provides a simple to use API to retrieve Expression instances efficiently. Issue: SPR-12622
1 parent cf86ecd commit c7b324b

File tree

10 files changed

+265
-104
lines changed

10 files changed

+265
-104
lines changed

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

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

27-
import org.springframework.cache.interceptor.MethodCacheKey;
27+
import org.springframework.context.expression.AnnotatedElementKey;
2828
import org.springframework.core.BridgeMethodResolver;
2929
import org.springframework.util.ClassUtils;
3030

@@ -57,7 +57,7 @@ public abstract class AbstractFallbackJCacheOperationSource
5757
@Override
5858
public JCacheOperation<?> getCacheOperation(Method method, Class<?> targetClass) {
5959
// First, see if we have a cached value.
60-
Object cacheKey = new MethodCacheKey(method, targetClass);
60+
Object cacheKey = new AnnotatedElementKey(method, targetClass);
6161
Object cached = this.cache.get(cacheKey);
6262
if (cached != null) {
6363
if (cached == NULL_CACHING_ATTRIBUTE) {

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

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

29+
import org.springframework.context.expression.AnnotatedElementKey;
2930
import org.springframework.core.BridgeMethodResolver;
3031
import org.springframework.util.ClassUtils;
3132

@@ -121,7 +122,7 @@ public Collection<CacheOperation> getCacheOperations(Method method, Class<?> tar
121122
* @return the cache key (never {@code null})
122123
*/
123124
protected Object getCacheKey(Method method, Class<?> targetClass) {
124-
return new MethodCacheKey(method, targetClass);
125+
return new AnnotatedElementKey(method, targetClass);
125126
}
126127

127128
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -39,6 +39,7 @@
3939
import org.springframework.cache.support.SimpleValueWrapper;
4040
import org.springframework.context.ApplicationContext;
4141
import org.springframework.context.ApplicationContextAware;
42+
import org.springframework.context.expression.AnnotatedElementKey;
4243
import org.springframework.expression.EvaluationContext;
4344
import org.springframework.util.Assert;
4445
import org.springframework.util.ClassUtils;
@@ -546,14 +547,14 @@ protected class CacheOperationContext implements CacheOperationInvocationContext
546547

547548
private final Collection<? extends Cache> caches;
548549

549-
private final MethodCacheKey methodCacheKey;
550+
private final AnnotatedElementKey methodCacheKey;
550551

551552
public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
552553
this.metadata = metadata;
553554
this.args = extractArgs(metadata.method, args);
554555
this.target = target;
555556
this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
556-
this.methodCacheKey = new MethodCacheKey(metadata.method, metadata.targetClass);
557+
this.methodCacheKey = new AnnotatedElementKey(metadata.method, metadata.targetClass);
557558
}
558559

559560
@Override
@@ -659,11 +660,11 @@ private static class CacheOperationCacheKey {
659660

660661
private final CacheOperation cacheOperation;
661662

662-
private final MethodCacheKey methodCacheKey;
663+
private final AnnotatedElementKey methodCacheKey;
663664

664665
private CacheOperationCacheKey(CacheOperation cacheOperation, Method method, Class<?> targetClass) {
665666
this.cacheOperation = cacheOperation;
666-
this.methodCacheKey = new MethodCacheKey(method, targetClass);
667+
this.methodCacheKey = new AnnotatedElementKey(method, targetClass);
667668
}
668669

669670
@Override

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323

2424
import org.springframework.aop.support.AopUtils;
25+
import org.springframework.context.expression.AnnotatedElementKey;
2526
import org.springframework.core.ParameterNameDiscoverer;
2627
import org.springframework.expression.spel.support.StandardEvaluationContext;
2728
import org.springframework.util.ObjectUtils;
@@ -53,15 +54,15 @@ class CacheEvaluationContext extends StandardEvaluationContext {
5354

5455
private final Class<?> targetClass;
5556

56-
private final Map<MethodCacheKey, Method> methodCache;
57+
private final Map<AnnotatedElementKey, Method> methodCache;
5758

5859
private final List<String> unavailableVariables;
5960

6061
private boolean paramLoaded = false;
6162

6263

6364
CacheEvaluationContext(Object rootObject, ParameterNameDiscoverer paramDiscoverer, Method method,
64-
Object[] args, Class<?> targetClass, Map<MethodCacheKey, Method> methodCache) {
65+
Object[] args, Class<?> targetClass, Map<AnnotatedElementKey, Method> methodCache) {
6566
super(rootObject);
6667

6768
this.paramDiscoverer = paramDiscoverer;
@@ -110,7 +111,7 @@ private void loadArgsAsVariables() {
110111
return;
111112
}
112113

113-
MethodCacheKey methodKey = new MethodCacheKey(this.method, this.targetClass);
114+
AnnotatedElementKey methodKey = new AnnotatedElementKey(this.method, this.targetClass);
114115
Method targetMethod = this.methodCache.get(methodKey);
115116
if (targetMethod == null) {
116117
targetMethod = AopUtils.getMostSpecificMethod(this.method, this.targetClass);
Lines changed: 13 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -22,27 +22,27 @@
2222
import java.util.concurrent.ConcurrentHashMap;
2323

2424
import org.springframework.cache.Cache;
25+
import org.springframework.context.expression.AnnotatedElementKey;
26+
import org.springframework.context.expression.CachedExpressionEvaluator;
2527
import org.springframework.core.DefaultParameterNameDiscoverer;
2628
import org.springframework.core.ParameterNameDiscoverer;
2729
import org.springframework.expression.EvaluationContext;
2830
import org.springframework.expression.Expression;
29-
import org.springframework.expression.spel.standard.SpelExpressionParser;
30-
import org.springframework.util.ObjectUtils;
3131

3232
/**
3333
* Utility class handling the SpEL expression parsing.
3434
* Meant to be used as a reusable, thread-safe component.
3535
*
3636
* <p>Performs internal caching for performance reasons
37-
* using {@link MethodCacheKey}.
37+
* using {@link AnnotatedElementKey}.
3838
*
3939
* @author Costin Leau
4040
* @author Phillip Webb
4141
* @author Sam Brannen
4242
* @author Stephane Nicoll
4343
* @since 3.1
4444
*/
45-
class ExpressionEvaluator {
45+
class ExpressionEvaluator extends CachedExpressionEvaluator {
4646

4747
/**
4848
* Indicate that there is no result variable.
@@ -59,9 +59,6 @@ class ExpressionEvaluator {
5959
*/
6060
public static final String RESULT_VARIABLE = "result";
6161

62-
63-
private final SpelExpressionParser parser = new SpelExpressionParser();
64-
6562
// shared param discoverer since it caches data internally
6663
private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
6764

@@ -71,7 +68,8 @@ class ExpressionEvaluator {
7168

7269
private final Map<ExpressionKey, Expression> unlessCache = new ConcurrentHashMap<ExpressionKey, Expression>(64);
7370

74-
private final Map<MethodCacheKey, Method> targetMethodCache = new ConcurrentHashMap<MethodCacheKey, Method>(64);
71+
private final Map<AnnotatedElementKey, Method> targetMethodCache =
72+
new ConcurrentHashMap<AnnotatedElementKey, Method>(64);
7573

7674

7775
/**
@@ -111,61 +109,17 @@ else if (result != NO_RESULT) {
111109
return evaluationContext;
112110
}
113111

114-
public Object key(String keyExpression, MethodCacheKey methodKey, EvaluationContext evalContext) {
115-
return getExpression(this.keyCache, keyExpression, methodKey).getValue(evalContext);
112+
public Object key(String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
113+
return getExpression(this.keyCache, methodKey, keyExpression).getValue(evalContext);
116114
}
117115

118-
public boolean condition(String conditionExpression, MethodCacheKey methodKey, EvaluationContext evalContext) {
119-
return getExpression(this.conditionCache, conditionExpression, methodKey).getValue(evalContext, boolean.class);
116+
public boolean condition(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
117+
return getExpression(this.conditionCache, methodKey, conditionExpression).getValue(evalContext, boolean.class);
120118
}
121119

122-
public boolean unless(String unlessExpression, MethodCacheKey methodKey, EvaluationContext evalContext) {
123-
return getExpression(this.unlessCache, unlessExpression, methodKey).getValue(evalContext, boolean.class);
124-
}
125-
126-
private Expression getExpression(Map<ExpressionKey, Expression> cache, String expression, MethodCacheKey methodKey) {
127-
ExpressionKey key = createKey(methodKey, expression);
128-
Expression expr = cache.get(key);
129-
if (expr == null) {
130-
expr = this.parser.parseExpression(expression);
131-
cache.put(key, expr);
132-
}
133-
return expr;
120+
public boolean unless(String unlessExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
121+
return getExpression(this.unlessCache, methodKey, unlessExpression).getValue(evalContext, boolean.class);
134122
}
135123

136-
private ExpressionKey createKey(MethodCacheKey methodCacheKey, String expression) {
137-
return new ExpressionKey(methodCacheKey, expression);
138-
}
139-
140-
141-
private static class ExpressionKey {
142-
143-
private final MethodCacheKey methodCacheKey;
144-
145-
private final String expression;
146-
147-
public ExpressionKey(MethodCacheKey methodCacheKey, String expression) {
148-
this.methodCacheKey = methodCacheKey;
149-
this.expression = expression;
150-
}
151-
152-
@Override
153-
public boolean equals(Object other) {
154-
if (this == other) {
155-
return true;
156-
}
157-
if (!(other instanceof ExpressionKey)) {
158-
return false;
159-
}
160-
ExpressionKey otherKey = (ExpressionKey) other;
161-
return (this.methodCacheKey.equals(otherKey.methodCacheKey) &&
162-
ObjectUtils.nullSafeEquals(this.expression, otherKey.expression));
163-
}
164-
165-
@Override
166-
public int hashCode() {
167-
return this.methodCacheKey.hashCode() * 29 + (this.expression != null ? this.expression.hashCode() : 0);
168-
}
169-
}
170124

171125
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -14,32 +14,35 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.cache.interceptor;
17+
package org.springframework.context.expression;
1818

19-
import java.lang.reflect.Method;
19+
import java.lang.reflect.AnnotatedElement;
2020

2121
import org.springframework.util.Assert;
2222
import org.springframework.util.ObjectUtils;
2323

2424
/**
25-
* Represent a method on a particular {@link Class} and is suitable as a key.
26-
*
27-
* <p>Mainly for internal use within the framework.
25+
* Represent an {@link AnnotatedElement} on a particular {@link Class}
26+
* and is suitable as a key.
2827
*
2928
* @author Costin Leau
3029
* @author Stephane Nicoll
31-
* @since 4.0.4
30+
* @since 4.2.0
31+
* @see CachedExpressionEvaluator
3232
*/
33-
public final class MethodCacheKey {
33+
public final class AnnotatedElementKey {
3434

35-
private final Method method;
35+
private final AnnotatedElement element;
3636

3737
private final Class<?> targetClass;
3838

39-
40-
public MethodCacheKey(Method method, Class<?> targetClass) {
41-
Assert.notNull(method, "method must be set.");
42-
this.method = method;
39+
/**
40+
* Create a new instance with the specified {@link AnnotatedElement} and
41+
* optional target {@link Class}.
42+
*/
43+
public AnnotatedElementKey(AnnotatedElement element, Class<?> targetClass) {
44+
Assert.notNull(element, "AnnotatedElement must be set.");
45+
this.element = element;
4346
this.targetClass = targetClass;
4447
}
4548

@@ -49,18 +52,17 @@ public boolean equals(Object other) {
4952
if (this == other) {
5053
return true;
5154
}
52-
if (!(other instanceof MethodCacheKey)) {
55+
if (!(other instanceof AnnotatedElementKey)) {
5356
return false;
5457
}
55-
MethodCacheKey otherKey = (MethodCacheKey) other;
56-
return (this.method.equals(otherKey.method) &&
58+
AnnotatedElementKey otherKey = (AnnotatedElementKey) other;
59+
return (this.element.equals(otherKey.element) &&
5760
ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
5861
}
5962

60-
6163
@Override
6264
public int hashCode() {
63-
return this.method.hashCode() + (this.targetClass != null ? this.targetClass.hashCode() * 29 : 0);
65+
return this.element.hashCode() + (this.targetClass != null ? this.targetClass.hashCode() * 29 : 0);
6466
}
6567

6668
}

0 commit comments

Comments
 (0)