提交 7efd54e2 编写于 作者: P Phillip Webb

Additional caching for ResolvableTypes

Add additional caching to ResolvableTypes and SerializableTypeWrapper
in order to improve SpEL performance.

Issue: SPR-11388
上级 319724f0
......@@ -115,6 +115,12 @@ public final class ResolvableType implements Serializable {
*/
private final Class<?> resolved;
private ResolvableType superType;
private ResolvableType[] interfaces;
private ResolvableType[] generics;
/**
* Private constructor used to create a new {@link ResolvableType} for resolution purposes.
......@@ -360,7 +366,11 @@ public final class ResolvableType implements Serializable {
if (resolved == null || resolved.getGenericSuperclass() == null) {
return NONE;
}
return forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver());
if (this.superType == null) {
this.superType = forType(SerializableTypeWrapper.forGenericSuperclass(resolved),
asVariableResolver());
}
return this.superType;
}
/**
......@@ -374,7 +384,11 @@ public final class ResolvableType implements Serializable {
if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) {
return EMPTY_TYPES_ARRAY;
}
return forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved), asVariableResolver());
if (this.interfaces == null) {
this.interfaces = forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved),
asVariableResolver());
}
return this.interfaces;
}
/**
......@@ -551,19 +565,24 @@ public final class ResolvableType implements Serializable {
if (this == NONE) {
return EMPTY_TYPES_ARRAY;
}
if (this.type instanceof Class<?>) {
Class<?> typeClass = (Class<?>) this.type;
return forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
}
if (this.type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
ResolvableType[] generics = new ResolvableType[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
generics[i] = forType(actualTypeArguments[i], this.variableResolver);
if (this.generics == null) {
if (this.type instanceof Class<?>) {
Class<?> typeClass = (Class<?>) this.type;
this.generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
}
else if (this.type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
ResolvableType[] generics = new ResolvableType[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
generics[i] = forType(actualTypeArguments[i], this.variableResolver);
}
this.generics = generics;
}
else {
this.generics = resolveType().getGenerics();
}
return generics;
}
return resolveType().getGenerics();
return this.generics;
}
/**
......
......@@ -31,6 +31,7 @@ import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
/**
......@@ -63,6 +64,9 @@ abstract class SerializableTypeWrapper {
private static final Method GET_TYPE_PROVIDER_METHOD = ReflectionUtils.findMethod(
SerializableTypeProxy.class, "getTypeProvider");
private static final ConcurrentReferenceHashMap<Type, Type> cache =
new ConcurrentReferenceHashMap<Type, Type>(256);
/**
* Return a {@link Serializable} variant of {@link Field#getGenericType()}.
*/
......@@ -150,13 +154,19 @@ abstract class SerializableTypeWrapper {
if (provider.getType() instanceof Serializable || provider.getType() == null) {
return provider.getType();
}
Type cached = cache.get(provider.getType());
if(cached != null) {
return cached;
}
for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) {
if (type.isAssignableFrom(provider.getType().getClass())) {
ClassLoader classLoader = provider.getClass().getClassLoader();
Class<?>[] interfaces = new Class<?>[] { type,
SerializableTypeProxy.class, Serializable.class };
InvocationHandler handler = new TypeProxyInvocationHandler(provider);
return (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
cache.put(provider.getType(), cached);
return cached;
}
}
throw new IllegalArgumentException("Unsupported Type class " + provider.getType().getClass().getName());
......
......@@ -20,7 +20,6 @@ import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
......@@ -29,6 +28,11 @@ import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.tests.Assume;
import org.springframework.tests.TestGroup;
import org.springframework.util.StopWatch;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
......@@ -96,6 +100,25 @@ public class MapAccessTests extends AbstractExpressionTests {
assertEquals("value", expr.getValue(map));
}
@Test
public void testGetValuePerformance() throws Exception {
Assume.group(TestGroup.PERFORMANCE);
Map<String, String> map = new HashMap<String, String>();
map.put("key", "value");
EvaluationContext context = new StandardEvaluationContext(map);
ExpressionParser spelExpressionParser = new SpelExpressionParser();
Expression expr = spelExpressionParser.parseExpression("#root['key']");
StopWatch s = new StopWatch();
s.start();
for (int i = 0; i < 10000; i++) {
expr.getValue(context);
}
s.stop();
assertThat(s.getTotalTimeMillis(), lessThan(200L));
}
public static class TestBean {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册