2929import org .springframework .core .convert .support .DefaultConversionService ;
3030import org .springframework .core .convert .support .GenericConversionService ;
3131import org .springframework .data .convert .Jsr310Converters ;
32+ import org .springframework .data .util .Lazy ;
3233import org .springframework .data .util .NullableWrapperConverters ;
3334import org .springframework .lang .Nullable ;
3435import org .springframework .util .Assert ;
3738
3839/**
3940 * 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.
4344 *
4445 * @author Oliver Gierke
4546 * @author Christoph Strobl
@@ -59,9 +60,12 @@ class ProxyProjectionFactory implements ProjectionFactory, BeanClassLoaderAware
5960 }
6061
6162 private final List <MethodInterceptorFactory > factories ;
62- private final Map <Class <?>, ProjectionInformation > projectionInformationCache = new ConcurrentReferenceHashMap <>();
63+ private final Map <Class <?>, ProjectionMetadata > projectionInformationCache = new ConcurrentReferenceHashMap <>();
6364 private @ Nullable ClassLoader classLoader ;
6465
66+ private final Lazy <DefaultMethodInvokingMethodInterceptor > defaultMethodInvokingMethodInterceptor = Lazy
67+ .of (DefaultMethodInvokingMethodInterceptor ::new );
68+
6569 /**
6670 * Creates a new {@link ProxyProjectionFactory}.
6771 */
@@ -108,7 +112,12 @@ public <T> T createProjection(Class<T> projectionType, Object source) {
108112 factory .setOpaque (true );
109113 factory .setInterfaces (projectionType , TargetAware .class );
110114
111- factory .addAdvice (new DefaultMethodInvokingMethodInterceptor ());
115+ ProjectionMetadata projectionMetadata = getProjectionMetadata (projectionType );
116+
117+ if (projectionMetadata .hasDefaultMethods ) {
118+ factory .addAdvice (defaultMethodInvokingMethodInterceptor .get ());
119+ }
120+
112121 factory .addAdvice (new TargetAwareMethodInterceptor (source .getClass ()));
113122 factory .addAdvice (getMethodInterceptor (source , projectionType ));
114123
@@ -125,8 +134,12 @@ public <T> T createProjection(Class<T> projectionType) {
125134
126135 @ Override
127136 public final ProjectionInformation getProjectionInformation (Class <?> projectionType ) {
137+ return getProjectionMetadata (projectionType ).projectionInformation ;
138+ }
128139
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 )));
130143 }
131144
132145 /**
@@ -193,13 +206,11 @@ private MethodInterceptorFactory getFactoryFor(Object source, Class<?> projectio
193206 *
194207 * @author Oliver Gierke
195208 */
196- private static class TargetAwareMethodInterceptor implements MethodInterceptor {
209+ private record TargetAwareMethodInterceptor ( Class <?> targetType ) implements MethodInterceptor {
197210
198211 private static final Method GET_TARGET_CLASS_METHOD ;
199212 private static final Method GET_TARGET_METHOD ;
200213
201- private final Class <?> targetType ;
202-
203214 static {
204215 try {
205216 GET_TARGET_CLASS_METHOD = TargetAware .class .getMethod ("getTargetClass" );
@@ -214,10 +225,9 @@ private static class TargetAwareMethodInterceptor implements MethodInterceptor {
214225 *
215226 * @param targetType must not be {@literal null}.
216227 */
217- public TargetAwareMethodInterceptor ( Class <?> targetType ) {
228+ private TargetAwareMethodInterceptor {
218229
219230 Assert .notNull (targetType , "Target type must not be null" );
220- this .targetType = targetType ;
221231 }
222232
223233 @ Nullable
@@ -239,7 +249,7 @@ public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) thro
239249 *
240250 * @author Oliver Gierke
241251 */
242- private static enum MapAccessingMethodInterceptorFactory implements MethodInterceptorFactory {
252+ private enum MapAccessingMethodInterceptorFactory implements MethodInterceptorFactory {
243253
244254 INSTANCE ;
245255
@@ -260,7 +270,7 @@ public boolean supports(Object source, Class<?> targetType) {
260270 *
261271 * @author Oliver Gierke
262272 */
263- private static enum PropertyAccessingMethodInvokerFactory implements MethodInterceptorFactory {
273+ private enum PropertyAccessingMethodInvokerFactory implements MethodInterceptorFactory {
264274
265275 INSTANCE ;
266276
@@ -274,4 +284,18 @@ public boolean supports(Object source, Class<?> targetType) {
274284 return true ;
275285 }
276286 }
287+
288+ /**
289+ * Holder for {@link ProjectionInformation} and whether the target projection type uses {@code default} interface
290+ * methods.
291+ *
292+ * @since 3.0.7
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+ }
277301}
0 commit comments