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
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.context.expression.AnnotatedElementKey;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -79,7 +80,7 @@ public class ExpressionEvaluatorTests {
|
||||
|
||||
Iterator<CacheOperation> it = ops.iterator();
|
||||
|
||||
MethodCacheKey key = new MethodCacheKey(method, AnnotatedClass.class);
|
||||
AnnotatedElementKey key = new AnnotatedElementKey(method, AnnotatedClass.class);
|
||||
|
||||
Object keyA = eval.key(it.next().getKey(), key, evalCtx);
|
||||
Object keyB = eval.key(it.next().getKey(), key, evalCtx);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
package org.springframework.context.expression;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@@ -29,7 +29,7 @@ import static org.junit.Assert.*;
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class MethodCacheKeyTests {
|
||||
public class AnnotatedElementKeyTests {
|
||||
|
||||
@Rule
|
||||
public final TestName name = new TestName();
|
||||
@@ -37,15 +37,15 @@ public class MethodCacheKeyTests {
|
||||
@Test
|
||||
public void sameInstanceEquals() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey instance = new MethodCacheKey(m, getClass());
|
||||
AnnotatedElementKey instance = new AnnotatedElementKey(m, getClass());
|
||||
assertKeyEquals(instance, instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey first = new MethodCacheKey(m, getClass());
|
||||
MethodCacheKey second = new MethodCacheKey(m, getClass());
|
||||
AnnotatedElementKey first = new AnnotatedElementKey(m, getClass());
|
||||
AnnotatedElementKey second = new AnnotatedElementKey(m, getClass());
|
||||
|
||||
assertKeyEquals(first, second);
|
||||
}
|
||||
@@ -53,8 +53,8 @@ public class MethodCacheKeyTests {
|
||||
@Test
|
||||
public void equalsNoTarget() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey first = new MethodCacheKey(m, null);
|
||||
MethodCacheKey second = new MethodCacheKey(m, null);
|
||||
AnnotatedElementKey first = new AnnotatedElementKey(m, null);
|
||||
AnnotatedElementKey second = new AnnotatedElementKey(m, null);
|
||||
|
||||
assertKeyEquals(first, second);
|
||||
}
|
||||
@@ -62,13 +62,13 @@ public class MethodCacheKeyTests {
|
||||
@Test
|
||||
public void noTargetClassNotEquals() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey first = new MethodCacheKey(m, getClass());
|
||||
MethodCacheKey second = new MethodCacheKey(m, null);
|
||||
AnnotatedElementKey first = new AnnotatedElementKey(m, getClass());
|
||||
AnnotatedElementKey second = new AnnotatedElementKey(m, null);
|
||||
|
||||
assertFalse(first.equals(second));
|
||||
}
|
||||
|
||||
protected void assertKeyEquals(MethodCacheKey first, MethodCacheKey second) {
|
||||
protected void assertKeyEquals(AnnotatedElementKey first, AnnotatedElementKey second) {
|
||||
assertEquals(first, second);
|
||||
assertEquals(first.hashCode(), second.hashCode());
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.expression;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CachedExpressionEvaluatorTests {
|
||||
|
||||
private final TestExpressionEvaluator expressionEvaluator = new TestExpressionEvaluator();
|
||||
|
||||
@Test
|
||||
public void parseNewExpression() {
|
||||
Method method = ReflectionUtils.findMethod(getClass(), "toString");
|
||||
Expression expression = expressionEvaluator.getTestExpression("true", method, getClass());
|
||||
hasParsedExpression("true");
|
||||
assertEquals(true, expression.getValue());
|
||||
assertEquals("Expression should be in cache", 1, expressionEvaluator.testCache.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheExpression() {
|
||||
Method method = ReflectionUtils.findMethod(getClass(), "toString");
|
||||
|
||||
expressionEvaluator.getTestExpression("true", method, getClass());
|
||||
expressionEvaluator.getTestExpression("true", method, getClass());
|
||||
expressionEvaluator.getTestExpression("true", method, getClass());
|
||||
hasParsedExpression("true");
|
||||
assertEquals("Only one expression should be in cache", 1, expressionEvaluator.testCache.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheExpressionBasedOnConcreteType() {
|
||||
Method method = ReflectionUtils.findMethod(getClass(), "toString");
|
||||
expressionEvaluator.getTestExpression("true", method, getClass());
|
||||
expressionEvaluator.getTestExpression("true", method, Object.class);
|
||||
assertEquals("Cached expression should be based on type", 2, expressionEvaluator.testCache.size());
|
||||
}
|
||||
|
||||
private void hasParsedExpression(String expression) {
|
||||
verify(expressionEvaluator.getParser(), times(1)).parseExpression(expression);
|
||||
}
|
||||
|
||||
private static class TestExpressionEvaluator extends CachedExpressionEvaluator {
|
||||
|
||||
private final Map<ExpressionKey, Expression> testCache = new ConcurrentHashMap<>();
|
||||
|
||||
public TestExpressionEvaluator() {
|
||||
super(mockSpelExpressionParser());
|
||||
}
|
||||
|
||||
public Expression getTestExpression(String expression, Method method, Class<?> type) {
|
||||
return getExpression(this.testCache, new AnnotatedElementKey(method, type), expression);
|
||||
}
|
||||
|
||||
private static SpelExpressionParser mockSpelExpressionParser() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
return spy(parser);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user