22
22
import java .io .ObjectOutputStream ;
23
23
import java .io .Serializable ;
24
24
import java .lang .reflect .Method ;
25
+ import java .util .function .Supplier ;
25
26
26
27
import org .aopalliance .intercept .MethodInterceptor ;
27
28
import org .aopalliance .intercept .MethodInvocation ;
28
29
import org .apache .commons .logging .Log ;
29
30
import org .apache .commons .logging .LogFactory ;
30
31
import org .springframework .aop .framework .ProxyFactory ;
32
+ import org .springframework .cglib .core .SpringNamingPolicy ;
31
33
import org .springframework .cglib .proxy .Callback ;
32
34
import org .springframework .cglib .proxy .Enhancer ;
33
35
import org .springframework .cglib .proxy .Factory ;
34
36
import org .springframework .cglib .proxy .MethodProxy ;
35
- import org .springframework .core .NativeDetector ;
36
37
import org .springframework .dao .DataAccessException ;
37
38
import org .springframework .dao .support .PersistenceExceptionTranslator ;
38
39
import org .springframework .data .mongodb .ClientSessionException ;
39
40
import org .springframework .data .mongodb .LazyLoadingException ;
40
41
import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty ;
41
42
import org .springframework .lang .Nullable ;
42
- import org .springframework .objenesis .ObjenesisStd ;
43
+ import org .springframework .objenesis .SpringObjenesis ;
43
44
import org .springframework .util .ReflectionUtils ;
44
45
45
46
import com .mongodb .DBRef ;
46
47
47
48
/**
48
49
* {@link ProxyFactory} to create a proxy for {@link MongoPersistentProperty#getType()} to resolve a reference lazily.
50
+ * <strong>NOTE</strong> This class is intended for internal usage only.
49
51
*
50
52
* @author Christoph Strobl
51
53
* @author Mark Paluch
52
54
*/
53
- class LazyLoadingProxyFactory {
55
+ public final class LazyLoadingProxyFactory {
54
56
55
57
private static final Log LOGGER = LogFactory .getLog (LazyLoadingProxyFactory .class );
56
58
57
- private final ObjenesisStd objenesis ;
59
+ private final SpringObjenesis objenesis ;
58
60
59
61
private final PersistenceExceptionTranslator exceptionTranslator ;
60
62
63
+ private LazyLoadingProxyFactory () {
64
+ this (ex -> null );
65
+ }
66
+
61
67
public LazyLoadingProxyFactory (PersistenceExceptionTranslator exceptionTranslator ) {
62
68
this .exceptionTranslator = exceptionTranslator ;
63
- this .objenesis = new ObjenesisStd ( true );
69
+ this .objenesis = new SpringObjenesis ( null );
64
70
}
65
71
66
- public Object createLazyLoadingProxy (MongoPersistentProperty property , DbRefResolverCallback callback ,
67
- Object source ) {
72
+ /**
73
+ * Predict the proxy target type. This will advice the infrastructure to resolve as many pieces as possible in a
74
+ * potential AOT scenario without necessarily resolving the entire object.
75
+ *
76
+ * @param propertyType the type to proxy
77
+ * @param interceptor the interceptor to be added.
78
+ * @return the proxy type.
79
+ * @since 4.0
80
+ */
81
+ public static Class <?> resolveProxyType (Class <?> propertyType , Supplier <LazyLoadingInterceptor > interceptor ) {
68
82
69
- Class <?> propertyType = property .getType ();
70
- LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor (property , callback , source , exceptionTranslator );
83
+ LazyLoadingProxyFactory factory = new LazyLoadingProxyFactory ();
71
84
72
85
if (!propertyType .isInterface ()) {
86
+ return factory .getEnhancedTypeFor (propertyType );
87
+ }
73
88
74
- if (NativeDetector .inNativeImage ()) {
75
-
76
- ProxyFactory factory = new ProxyFactory ();
77
- factory .addAdvice (interceptor );
78
- factory .addInterface (LazyLoadingProxy .class );
79
- factory .setTargetClass (propertyType );
80
- factory .setProxyTargetClass (true );
81
- return factory .getProxy (propertyType .getClassLoader ());
82
- }
83
-
84
- Factory factory = (Factory ) objenesis .newInstance (getEnhancedTypeFor (propertyType ));
85
- factory .setCallbacks (new Callback [] { interceptor });
89
+ return factory .prepareProxyFactory (propertyType , interceptor )
90
+ .getProxyClass (LazyLoadingProxy .class .getClassLoader ());
91
+ }
86
92
87
- return factory ;
88
- }
93
+ private ProxyFactory prepareProxyFactory (Class <?> propertyType , Supplier <LazyLoadingInterceptor > interceptor ) {
89
94
90
95
ProxyFactory proxyFactory = new ProxyFactory ();
91
96
@@ -95,9 +100,27 @@ public Object createLazyLoadingProxy(MongoPersistentProperty property, DbRefReso
95
100
96
101
proxyFactory .addInterface (LazyLoadingProxy .class );
97
102
proxyFactory .addInterface (propertyType );
98
- proxyFactory .addAdvice (interceptor );
103
+ proxyFactory .addAdvice (interceptor .get ());
104
+
105
+ return proxyFactory ;
106
+ }
107
+
108
+ public Object createLazyLoadingProxy (MongoPersistentProperty property , DbRefResolverCallback callback ,
109
+ Object source ) {
110
+
111
+ Class <?> propertyType = property .getType ();
112
+ LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor (property , callback , source , exceptionTranslator );
113
+
114
+ if (!propertyType .isInterface ()) {
99
115
100
- return proxyFactory .getProxy (LazyLoadingProxy .class .getClassLoader ());
116
+ Factory factory = (Factory ) objenesis .newInstance (getEnhancedTypeFor (propertyType ));
117
+ factory .setCallbacks (new Callback [] { interceptor });
118
+
119
+ return factory ;
120
+ }
121
+
122
+ return prepareProxyFactory (propertyType ,
123
+ () -> new LazyLoadingInterceptor (property , callback , source , exceptionTranslator )).getProxy ();
101
124
}
102
125
103
126
/**
@@ -110,8 +133,10 @@ private Class<?> getEnhancedTypeFor(Class<?> type) {
110
133
111
134
Enhancer enhancer = new Enhancer ();
112
135
enhancer .setSuperclass (type );
113
- enhancer .setCallbackType (org . springframework . cglib . proxy . MethodInterceptor .class );
136
+ enhancer .setCallbackType (LazyLoadingInterceptor .class );
114
137
enhancer .setInterfaces (new Class [] { LazyLoadingProxy .class });
138
+ enhancer .setNamingPolicy (SpringNamingPolicy .INSTANCE );
139
+ enhancer .setAttemptLoad (true );
115
140
116
141
return enhancer .createClass ();
117
142
}
@@ -139,6 +164,29 @@ public static class LazyLoadingInterceptor
139
164
private volatile boolean resolved ;
140
165
private @ Nullable Object result ;
141
166
167
+ /**
168
+ * @return a {@link LazyLoadingInterceptor} that just continues with the invocation.
169
+ * @since 4.0
170
+ */
171
+ public static LazyLoadingInterceptor none () {
172
+
173
+ return new LazyLoadingInterceptor (null , null , null , null ) {
174
+ @ Nullable
175
+ @ Override
176
+ public Object invoke (MethodInvocation invocation ) throws Throwable {
177
+ return intercept (invocation .getThis (), invocation .getMethod (), invocation .getArguments (), null );
178
+ }
179
+
180
+ @ Nullable
181
+ @ Override
182
+ public Object intercept (Object o , Method method , Object [] args , MethodProxy proxy ) throws Throwable {
183
+
184
+ ReflectionUtils .makeAccessible (method );
185
+ return method .invoke (o , args );
186
+ }
187
+ };
188
+ }
189
+
142
190
public LazyLoadingInterceptor (MongoPersistentProperty property , DbRefResolverCallback callback , Object source ,
143
191
PersistenceExceptionTranslator exceptionTranslator ) {
144
192
0 commit comments