1
16 package org.springframework.data.projection;
17
18 import java.beans.PropertyDescriptor;
19 import java.lang.reflect.Method;
20 import java.util.Map;
21 import java.util.concurrent.ConcurrentHashMap;
22
23 import org.aopalliance.intercept.MethodInterceptor;
24 import org.springframework.beans.BeansException;
25 import org.springframework.beans.factory.BeanFactory;
26 import org.springframework.beans.factory.BeanFactoryAware;
27 import org.springframework.beans.factory.annotation.Value;
28 import org.springframework.core.annotation.AnnotationUtils;
29 import org.springframework.data.util.AnnotationDetectionMethodCallback;
30 import org.springframework.expression.spel.standard.SpelExpressionParser;
31 import org.springframework.lang.Nullable;
32 import org.springframework.util.Assert;
33 import org.springframework.util.ReflectionUtils;
34
35
45 public class SpelAwareProxyProjectionFactory extends ProxyProjectionFactory implements BeanFactoryAware {
46
47 private final Map<Class<?>, Boolean> typeCache = new ConcurrentHashMap<>();
48 private final SpelExpressionParser parser = new SpelExpressionParser();
49
50 private @Nullable BeanFactory beanFactory;
51
52
56 @Override
57 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
58 this.beanFactory = beanFactory;
59 }
60
61
65 @Override
66 protected ProjectionInformation createProjectionInformation(Class<?> projectionType) {
67 return new SpelAwareProjectionInformation(projectionType);
68 }
69
70
79 @Override
80 protected MethodInterceptor postProcessAccessorInterceptor(MethodInterceptor interceptor, Object source,
81 Class<?> projectionType) {
82
83 return typeCache.computeIfAbsent(projectionType, SpelAwareProxyProjectionFactory::hasMethodWithValueAnnotation)
84 ? new SpelEvaluatingMethodInterceptor(interceptor, source, beanFactory, parser, projectionType)
85 : interceptor;
86 }
87
88
94 private static boolean hasMethodWithValueAnnotation(Class<?> type) {
95
96 Assert.notNull(type, "Type must not be null!");
97
98 AnnotationDetectionMethodCallback<Value> callback = new AnnotationDetectionMethodCallback<>(Value.class);
99 ReflectionUtils.doWithMethods(type, callback);
100
101 return callback.hasFoundAnnotation();
102 }
103
104 protected static class SpelAwareProjectionInformation extends DefaultProjectionInformation {
105
106 protected SpelAwareProjectionInformation(Class<?> projectionType) {
107 super(projectionType);
108 }
109
110
114 @Override
115 protected boolean isInputProperty(PropertyDescriptor descriptor) {
116
117 if (!super.isInputProperty(descriptor)) {
118 return false;
119 }
120
121 Method readMethod = descriptor.getReadMethod();
122
123 if (readMethod == null) {
124 return false;
125 }
126
127 return AnnotationUtils.findAnnotation(readMethod, Value.class) == null;
128 }
129 }
130 }
131