DATACMNS-1107 - ProxyProjectionFactory now caches ProjectionInformation instances.

To avoid the repeated scanning of projection interfaces for property descriptors we now cache the ProjectionInformation instances created via ProjectionFactory.getProjectionInformation(…). Adapted SpelAwareProxyProjectionFactory to now apply its customizations in newly introduced createProjectionInformation(…).
This commit is contained in:
Oliver Gierke
2017-07-11 11:21:40 +02:00
parent 8026e9ff72
commit 6e09c09afa
2 changed files with 35 additions and 13 deletions

View File

@@ -30,6 +30,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* A {@link ProjectionFactory} to create JDK proxies to back interfaces and handle method invocations on them. By
@@ -46,6 +47,7 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
private final List<MethodInterceptorFactory> factories;
private final ConversionService conversionService;
private final Map<Class<?>, ProjectionInformation> projectionInformationCache = new ConcurrentReferenceHashMap<>();
private ClassLoader classLoader;
/**
@@ -127,7 +129,18 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
* @see org.springframework.data.projection.ProjectionFactory#getProjectionInformation(java.lang.Class)
*/
@Override
public ProjectionInformation getProjectionInformation(Class<?> projectionType) {
public final ProjectionInformation getProjectionInformation(Class<?> projectionType) {
return projectionInformationCache.computeIfAbsent(projectionType, this::createProjectionInformation);
}
/**
* Creates a fresh, cacheable {@link ProjectionInformation} instance for the given projection type.
*
* @param projectionType must not be {@literal null}.
* @return
*/
protected ProjectionInformation createProjectionInformation(Class<?> projectionType) {
return new DefaultProjectionInformation(projectionType);
}

View File

@@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.util.AnnotationDetectionMethodCallback;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
@@ -68,25 +69,17 @@ public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory impl
protected MethodInterceptor postProcessAccessorInterceptor(MethodInterceptor interceptor, Object source,
Class<?> projectionType) {
if (!typeCache.containsKey(projectionType)) {
AnnotationDetectionMethodCallback<Value> callback = new AnnotationDetectionMethodCallback<>(Value.class);
ReflectionUtils.doWithMethods(projectionType, callback);
typeCache.put(projectionType, callback.hasFoundAnnotation());
}
return typeCache.get(projectionType)
return typeCache.computeIfAbsent(projectionType, SpelAwareProxyProjectionFactory::hasMethodWithValueAnnotation)
? new SpelEvaluatingMethodInterceptor(interceptor, source, beanFactory, parser, projectionType)
: interceptor;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.projection.ProxyProjectionFactory#getProjectionInformation(java.lang.Class)
* @see org.springframework.data.projection.ProxyProjectionFactory#createProjectionInformation(java.lang.Class)
*/
@Override
public ProjectionInformation getProjectionInformation(Class<?> projectionType) {
protected ProjectionInformation createProjectionInformation(Class<?> projectionType) {
return new DefaultProjectionInformation(projectionType) {
@@ -111,4 +104,20 @@ public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory impl
}
};
}
/**
* Returns whether the given type as a method annotated with {@link Value}.
*
* @param type must not be {@literal null}.
* @return
*/
private static boolean hasMethodWithValueAnnotation(Class<?> type) {
Assert.notNull(type, "Type must not be null!");
AnnotationDetectionMethodCallback<Value> callback = new AnnotationDetectionMethodCallback<>(Value.class);
ReflectionUtils.doWithMethods(type, callback);
return callback.hasFoundAnnotation();
}
}