29
29
import org .springframework .core .convert .support .DefaultConversionService ;
30
30
import org .springframework .core .convert .support .GenericConversionService ;
31
31
import org .springframework .data .convert .Jsr310Converters ;
32
+ import org .springframework .data .util .Lazy ;
32
33
import org .springframework .data .util .NullableWrapperConverters ;
33
34
import org .springframework .lang .Nullable ;
34
35
import org .springframework .util .Assert ;
37
38
38
39
/**
39
40
* A {@link ProjectionFactory} to create JDK proxies to back interfaces and handle method invocations on them. By
40
- * default accessor methods are supported. In case the delegating lookups result in an object of different type that the
41
- * projection interface method's return type, another projection will be created to transparently mitigate between the
42
- * types.
41
+ * default, accessor methods are supported. In case the delegating lookups result in an object of different type that
42
+ * the projection interface method's return type, another projection will be created to transparently mitigate between
43
+ * the types.
43
44
*
44
45
* @author Oliver Gierke
45
46
* @author Christoph Strobl
@@ -59,9 +60,12 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
59
60
}
60
61
61
62
private final List <MethodInterceptorFactory > factories ;
62
- private final Map <Class <?>, ProjectionInformation > projectionInformationCache = new ConcurrentReferenceHashMap <>();
63
+ private final Map <Class <?>, ProjectionMetadata > projectionInformationCache = new ConcurrentReferenceHashMap <>();
63
64
private @ Nullable ClassLoader classLoader ;
64
65
66
+ private final Lazy <DefaultMethodInvokingMethodInterceptor > defaultMethodInvokingMethodInterceptor = Lazy
67
+ .of (DefaultMethodInvokingMethodInterceptor ::new );
68
+
65
69
/**
66
70
* Creates a new {@link ProxyProjectionFactory}.
67
71
*/
@@ -108,7 +112,12 @@ public <T> T createProjection(Class<T> projectionType, Object source) {
108
112
factory .setOpaque (true );
109
113
factory .setInterfaces (projectionType , TargetAware .class );
110
114
111
- factory .addAdvice (new DefaultMethodInvokingMethodInterceptor ());
115
+ ProjectionMetadata projectionMetadata = getProjectionMetadata (projectionType );
116
+
117
+ if (projectionMetadata .hasDefaultMethods ) {
118
+ factory .addAdvice (defaultMethodInvokingMethodInterceptor .get ());
119
+ }
120
+
112
121
factory .addAdvice (new TargetAwareMethodInterceptor (source .getClass ()));
113
122
factory .addAdvice (getMethodInterceptor (source , projectionType ));
114
123
@@ -125,8 +134,12 @@ public <T> T createProjection(Class<T> projectionType) {
125
134
126
135
@ Override
127
136
public final ProjectionInformation getProjectionInformation (Class <?> projectionType ) {
137
+ return getProjectionMetadata (projectionType ).projectionInformation ;
138
+ }
128
139
129
- return projectionInformationCache .computeIfAbsent (projectionType , this ::createProjectionInformation );
140
+ private ProjectionMetadata getProjectionMetadata (Class <?> projectionType ) {
141
+ return projectionInformationCache .computeIfAbsent (projectionType ,
142
+ it -> ProjectionMetadata .create (it , createProjectionInformation (it )));
130
143
}
131
144
132
145
/**
@@ -193,13 +206,11 @@ private MethodInterceptorFactory getFactoryFor(Object source, Class<?> projectio
193
206
*
194
207
* @author Oliver Gierke
195
208
*/
196
- private static class TargetAwareMethodInterceptor implements MethodInterceptor {
209
+ private record TargetAwareMethodInterceptor ( Class <?> targetType ) implements MethodInterceptor {
197
210
198
211
private static final Method GET_TARGET_CLASS_METHOD ;
199
212
private static final Method GET_TARGET_METHOD ;
200
213
201
- private final Class <?> targetType ;
202
-
203
214
static {
204
215
try {
205
216
GET_TARGET_CLASS_METHOD = TargetAware .class .getMethod ("getTargetClass" );
@@ -214,10 +225,9 @@ private static class TargetAwareMethodInterceptor implements MethodInterceptor {
214
225
*
215
226
* @param targetType must not be {@literal null}.
216
227
*/
217
- public TargetAwareMethodInterceptor ( Class <?> targetType ) {
228
+ private TargetAwareMethodInterceptor {
218
229
219
230
Assert .notNull (targetType , "Target type must not be null" );
220
- this .targetType = targetType ;
221
231
}
222
232
223
233
@ Nullable
@@ -239,7 +249,7 @@ public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) thro
239
249
*
240
250
* @author Oliver Gierke
241
251
*/
242
- private static enum MapAccessingMethodInterceptorFactory implements MethodInterceptorFactory {
252
+ private enum MapAccessingMethodInterceptorFactory implements MethodInterceptorFactory {
243
253
244
254
INSTANCE ;
245
255
@@ -260,7 +270,7 @@ public boolean supports(Object source, Class<?> targetType) {
260
270
*
261
271
* @author Oliver Gierke
262
272
*/
263
- private static enum PropertyAccessingMethodInvokerFactory implements MethodInterceptorFactory {
273
+ private enum PropertyAccessingMethodInvokerFactory implements MethodInterceptorFactory {
264
274
265
275
INSTANCE ;
266
276
@@ -274,4 +284,18 @@ public boolean supports(Object source, Class<?> targetType) {
274
284
return true ;
275
285
}
276
286
}
287
+
288
+ /**
289
+ * Holder for {@link ProjectionInformation} and whether the target projection type uses {@code default} interface
290
+ * methods.
291
+ *
292
+ * @since 3.1.1
293
+ */
294
+ record ProjectionMetadata (boolean hasDefaultMethods , ProjectionInformation projectionInformation ) {
295
+
296
+ public static ProjectionMetadata create (Class <?> projectionType , ProjectionInformation projectionInformation ) {
297
+ return new ProjectionMetadata (DefaultMethodInvokingMethodInterceptor .hasDefaultMethods (projectionType ),
298
+ projectionInformation );
299
+ }
300
+ }
277
301
}
0 commit comments