From 6e09c09afae3165346e0499bd17378348a9012f9 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 11 Jul 2017 11:21:40 +0200 Subject: [PATCH] DATACMNS-1107 - ProxyProjectionFactory now caches ProjectionInformation instances. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(…). --- .../projection/ProxyProjectionFactory.java | 15 ++++++++- .../SpelAwareProxyProjectionFactory.java | 33 ++++++++++++------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/springframework/data/projection/ProxyProjectionFactory.java b/src/main/java/org/springframework/data/projection/ProxyProjectionFactory.java index 95191927e..975ac66ff 100644 --- a/src/main/java/org/springframework/data/projection/ProxyProjectionFactory.java +++ b/src/main/java/org/springframework/data/projection/ProxyProjectionFactory.java @@ -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 factories; private final ConversionService conversionService; + private final Map, 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); } diff --git a/src/main/java/org/springframework/data/projection/SpelAwareProxyProjectionFactory.java b/src/main/java/org/springframework/data/projection/SpelAwareProxyProjectionFactory.java index 07aa06bab..8a28c184a 100644 --- a/src/main/java/org/springframework/data/projection/SpelAwareProxyProjectionFactory.java +++ b/src/main/java/org/springframework/data/projection/SpelAwareProxyProjectionFactory.java @@ -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 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 callback = new AnnotationDetectionMethodCallback<>(Value.class); + ReflectionUtils.doWithMethods(type, callback); + + return callback.hasFoundAnnotation(); + } }