15
15
*/
16
16
package org .springframework .data .mongodb .core .convert ;
17
17
18
- import static org .springframework .util .ReflectionUtils .*;
19
-
20
- import java .io .IOException ;
21
- import java .io .ObjectInputStream ;
22
- import java .io .ObjectOutputStream ;
23
- import java .io .Serializable ;
24
- import java .lang .reflect .Method ;
25
18
import java .util .ArrayList ;
26
19
import java .util .Collection ;
27
20
import java .util .Collections ;
28
21
import java .util .List ;
29
22
import java .util .stream .Collectors ;
30
23
import java .util .stream .Stream ;
31
24
32
- import org .aopalliance .intercept .MethodInterceptor ;
33
- import org .aopalliance .intercept .MethodInvocation ;
34
25
import org .bson .Document ;
35
26
import org .slf4j .Logger ;
36
27
import org .slf4j .LoggerFactory ;
37
- import org .springframework .aop .framework .ProxyFactory ;
38
- import org .springframework .cglib .proxy .Callback ;
39
- import org .springframework .cglib .proxy .Enhancer ;
40
- import org .springframework .cglib .proxy .Factory ;
41
- import org .springframework .cglib .proxy .MethodProxy ;
42
- import org .springframework .dao .DataAccessException ;
28
+
43
29
import org .springframework .dao .InvalidDataAccessApiUsageException ;
44
- import org .springframework .dao .support .PersistenceExceptionTranslator ;
45
- import org .springframework .data .mongodb .ClientSessionException ;
46
- import org .springframework .data .mongodb .LazyLoadingException ;
47
30
import org .springframework .data .mongodb .MongoDatabaseFactory ;
48
31
import org .springframework .data .mongodb .MongoDatabaseUtils ;
49
32
import org .springframework .data .mongodb .core .convert .ReferenceLoader .DocumentReferenceQuery ;
50
33
import org .springframework .data .mongodb .core .mapping .BasicMongoPersistentProperty ;
51
34
import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty ;
52
35
import org .springframework .lang .Nullable ;
53
- import org .springframework .objenesis .ObjenesisStd ;
54
36
import org .springframework .util .Assert ;
55
- import org .springframework .util .ReflectionUtils ;
56
37
import org .springframework .util .StringUtils ;
57
38
58
39
import com .mongodb .DBRef ;
@@ -74,8 +55,6 @@ public class DefaultDbRefResolver extends DefaultReferenceResolver implements Db
74
55
private static final Logger LOGGER = LoggerFactory .getLogger (DefaultDbRefResolver .class );
75
56
76
57
private final MongoDatabaseFactory mongoDbFactory ;
77
- private final PersistenceExceptionTranslator exceptionTranslator ;
78
- private final ObjenesisStd objenesis ;
79
58
80
59
/**
81
60
* Creates a new {@link DefaultDbRefResolver} with the given {@link MongoDatabaseFactory}.
@@ -84,13 +63,11 @@ public class DefaultDbRefResolver extends DefaultReferenceResolver implements Db
84
63
*/
85
64
public DefaultDbRefResolver (MongoDatabaseFactory mongoDbFactory ) {
86
65
87
- super (new MongoDatabaseFactoryReferenceLoader (mongoDbFactory ));
66
+ super (new MongoDatabaseFactoryReferenceLoader (mongoDbFactory ), mongoDbFactory . getExceptionTranslator () );
88
67
89
68
Assert .notNull (mongoDbFactory , "MongoDbFactory translator must not be null!" );
90
69
91
70
this .mongoDbFactory = mongoDbFactory ;
92
- this .exceptionTranslator = mongoDbFactory .getExceptionTranslator ();
93
- this .objenesis = new ObjenesisStd (true );
94
71
}
95
72
96
73
/*
@@ -180,44 +157,9 @@ public List<Document> bulkFetch(List<DBRef> refs) {
180
157
private Object createLazyLoadingProxy (MongoPersistentProperty property , @ Nullable DBRef dbref ,
181
158
DbRefResolverCallback callback , DbRefProxyHandler handler ) {
182
159
183
- Class <?> propertyType = property .getType ();
184
- LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor (property , dbref , exceptionTranslator , callback );
185
-
186
- if (!propertyType .isInterface ()) {
187
-
188
- Factory factory = (Factory ) objenesis .newInstance (getEnhancedTypeFor (propertyType ));
189
- factory .setCallbacks (new Callback [] { interceptor });
190
-
191
- return handler .populateId (property , dbref , factory );
192
- }
193
-
194
- ProxyFactory proxyFactory = new ProxyFactory ();
195
-
196
- for (Class <?> type : propertyType .getInterfaces ()) {
197
- proxyFactory .addInterface (type );
198
- }
199
-
200
- proxyFactory .addInterface (LazyLoadingProxy .class );
201
- proxyFactory .addInterface (propertyType );
202
- proxyFactory .addAdvice (interceptor );
160
+ Object lazyLoadingProxy = getProxyFactory ().createLazyLoadingProxy (property , callback , dbref );
203
161
204
- return handler .populateId (property , dbref , proxyFactory .getProxy (LazyLoadingProxy .class .getClassLoader ()));
205
- }
206
-
207
- /**
208
- * Returns the CGLib enhanced type for the given source type.
209
- *
210
- * @param type
211
- * @return
212
- */
213
- private Class <?> getEnhancedTypeFor (Class <?> type ) {
214
-
215
- Enhancer enhancer = new Enhancer ();
216
- enhancer .setSuperclass (type );
217
- enhancer .setCallbackType (org .springframework .cglib .proxy .MethodInterceptor .class );
218
- enhancer .setInterfaces (new Class [] { LazyLoadingProxy .class });
219
-
220
- return enhancer .createClass ();
162
+ return handler .populateId (property , dbref , lazyLoadingProxy );
221
163
}
222
164
223
165
/**
@@ -244,249 +186,6 @@ private static Stream<Document> documentWithId(Object identifier, Collection<Doc
244
186
.limit (1 );
245
187
}
246
188
247
- /**
248
- * A {@link MethodInterceptor} that is used within a lazy loading proxy. The property resolving is delegated to a
249
- * {@link DbRefResolverCallback}. The resolving process is triggered by a method invocation on the proxy and is
250
- * guaranteed to be performed only once.
251
- *
252
- * @author Thomas Darimont
253
- * @author Oliver Gierke
254
- * @author Christoph Strobl
255
- */
256
- static class LazyLoadingInterceptor
257
- implements MethodInterceptor , org .springframework .cglib .proxy .MethodInterceptor , Serializable {
258
-
259
- private static final Method INITIALIZE_METHOD , TO_DBREF_METHOD , FINALIZE_METHOD ;
260
-
261
- private final DbRefResolverCallback callback ;
262
- private final MongoPersistentProperty property ;
263
- private final PersistenceExceptionTranslator exceptionTranslator ;
264
-
265
- private volatile boolean resolved ;
266
- private final @ Nullable DBRef dbref ;
267
- private @ Nullable Object result ;
268
-
269
- static {
270
- try {
271
- INITIALIZE_METHOD = LazyLoadingProxy .class .getMethod ("getTarget" );
272
- TO_DBREF_METHOD = LazyLoadingProxy .class .getMethod ("toDBRef" );
273
- FINALIZE_METHOD = Object .class .getDeclaredMethod ("finalize" );
274
- } catch (Exception e ) {
275
- throw new RuntimeException (e );
276
- }
277
- }
278
-
279
- /**
280
- * Creates a new {@link LazyLoadingInterceptor} for the given {@link MongoPersistentProperty},
281
- * {@link PersistenceExceptionTranslator} and {@link DbRefResolverCallback}.
282
- *
283
- * @param property must not be {@literal null}.
284
- * @param dbref can be {@literal null}.
285
- * @param callback must not be {@literal null}.
286
- */
287
- public LazyLoadingInterceptor (MongoPersistentProperty property , @ Nullable DBRef dbref ,
288
- PersistenceExceptionTranslator exceptionTranslator , DbRefResolverCallback callback ) {
289
-
290
- Assert .notNull (property , "Property must not be null!" );
291
- Assert .notNull (exceptionTranslator , "Exception translator must not be null!" );
292
- Assert .notNull (callback , "Callback must not be null!" );
293
-
294
- this .dbref = dbref ;
295
- this .callback = callback ;
296
- this .exceptionTranslator = exceptionTranslator ;
297
- this .property = property ;
298
- }
299
-
300
- /*
301
- * (non-Javadoc)
302
- * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
303
- */
304
- @ Override
305
- public Object invoke (@ Nullable MethodInvocation invocation ) throws Throwable {
306
- return intercept (invocation .getThis (), invocation .getMethod (), invocation .getArguments (), null );
307
- }
308
-
309
- /*
310
- * (non-Javadoc)
311
- * @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy)
312
- */
313
- @ Nullable
314
- @ Override
315
- public Object intercept (Object obj , Method method , Object [] args , @ Nullable MethodProxy proxy ) throws Throwable {
316
-
317
- if (INITIALIZE_METHOD .equals (method )) {
318
- return ensureResolved ();
319
- }
320
-
321
- if (TO_DBREF_METHOD .equals (method )) {
322
- return this .dbref ;
323
- }
324
-
325
- if (isObjectMethod (method ) && Object .class .equals (method .getDeclaringClass ())) {
326
-
327
- if (ReflectionUtils .isToStringMethod (method )) {
328
- return proxyToString (proxy );
329
- }
330
-
331
- if (ReflectionUtils .isEqualsMethod (method )) {
332
- return proxyEquals (proxy , args [0 ]);
333
- }
334
-
335
- if (ReflectionUtils .isHashCodeMethod (method )) {
336
- return proxyHashCode (proxy );
337
- }
338
-
339
- // DATAMONGO-1076 - finalize methods should not trigger proxy initialization
340
- if (FINALIZE_METHOD .equals (method )) {
341
- return null ;
342
- }
343
- }
344
-
345
- Object target = ensureResolved ();
346
-
347
- if (target == null ) {
348
- return null ;
349
- }
350
-
351
- ReflectionUtils .makeAccessible (method );
352
-
353
- return method .invoke (target , args );
354
- }
355
-
356
- /**
357
- * Returns a to string representation for the given {@code proxy}.
358
- *
359
- * @param proxy
360
- * @return
361
- */
362
- private String proxyToString (@ Nullable Object proxy ) {
363
-
364
- StringBuilder description = new StringBuilder ();
365
- if (dbref != null ) {
366
- description .append (dbref .getCollectionName ());
367
- description .append (":" );
368
- description .append (dbref .getId ());
369
- } else {
370
- description .append (System .identityHashCode (proxy ));
371
- }
372
- description .append ("$" ).append (LazyLoadingProxy .class .getSimpleName ());
373
-
374
- return description .toString ();
375
- }
376
-
377
- /**
378
- * Returns the hashcode for the given {@code proxy}.
379
- *
380
- * @param proxy
381
- * @return
382
- */
383
- private int proxyHashCode (@ Nullable Object proxy ) {
384
- return proxyToString (proxy ).hashCode ();
385
- }
386
-
387
- /**
388
- * Performs an equality check for the given {@code proxy}.
389
- *
390
- * @param proxy
391
- * @param that
392
- * @return
393
- */
394
- private boolean proxyEquals (@ Nullable Object proxy , Object that ) {
395
-
396
- if (!(that instanceof LazyLoadingProxy )) {
397
- return false ;
398
- }
399
-
400
- if (that == proxy ) {
401
- return true ;
402
- }
403
-
404
- return proxyToString (proxy ).equals (that .toString ());
405
- }
406
-
407
- /**
408
- * Will trigger the resolution if the proxy is not resolved already or return a previously resolved result.
409
- *
410
- * @return
411
- */
412
- @ Nullable
413
- private Object ensureResolved () {
414
-
415
- if (!resolved ) {
416
- this .result = resolve ();
417
- this .resolved = true ;
418
- }
419
-
420
- return this .result ;
421
- }
422
-
423
- /**
424
- * Callback method for serialization.
425
- *
426
- * @param out
427
- * @throws IOException
428
- */
429
- private void writeObject (ObjectOutputStream out ) throws IOException {
430
-
431
- ensureResolved ();
432
- out .writeObject (this .result );
433
- }
434
-
435
- /**
436
- * Callback method for deserialization.
437
- *
438
- * @param in
439
- * @throws IOException
440
- */
441
- private void readObject (ObjectInputStream in ) throws IOException {
442
-
443
- try {
444
- this .resolved = true ;
445
- this .result = in .readObject ();
446
- } catch (ClassNotFoundException e ) {
447
- throw new LazyLoadingException ("Could not deserialize result" , e );
448
- }
449
- }
450
-
451
- /**
452
- * Resolves the proxy into its backing object.
453
- *
454
- * @return
455
- */
456
- @ Nullable
457
- private synchronized Object resolve () {
458
-
459
- if (resolved ) {
460
-
461
- if (LOGGER .isTraceEnabled ()) {
462
- LOGGER .trace ("Accessing already resolved lazy loading property {}.{}" ,
463
- property .getOwner () != null ? property .getOwner ().getName () : "unknown" , property .getName ());
464
- }
465
- return result ;
466
- }
467
-
468
- try {
469
- if (LOGGER .isTraceEnabled ()) {
470
- LOGGER .trace ("Resolving lazy loading property {}.{}" ,
471
- property .getOwner () != null ? property .getOwner ().getName () : "unknown" , property .getName ());
472
- }
473
-
474
- return callback .resolve (property );
475
-
476
- } catch (RuntimeException ex ) {
477
-
478
- DataAccessException translatedException = this .exceptionTranslator .translateExceptionIfPossible (ex );
479
-
480
- if (translatedException instanceof ClientSessionException ) {
481
- throw new LazyLoadingException ("Unable to lazily resolve DBRef! Invalid session state." , ex );
482
- }
483
-
484
- throw new LazyLoadingException ("Unable to lazily resolve DBRef!" ,
485
- translatedException != null ? translatedException : ex );
486
- }
487
- }
488
- }
489
-
490
189
/**
491
190
* Customization hook for obtaining the {@link MongoCollection} for a given {@link DBRef}.
492
191
*
0 commit comments