1515 */
1616package org .springframework .data .mongodb .core .convert ;
1717
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 ;
2518import java .util .ArrayList ;
2619import java .util .Collection ;
2720import java .util .Collections ;
2821import java .util .List ;
2922import java .util .stream .Collectors ;
3023import java .util .stream .Stream ;
3124
32- import org .aopalliance .intercept .MethodInterceptor ;
33- import org .aopalliance .intercept .MethodInvocation ;
3425import org .bson .Document ;
3526import org .slf4j .Logger ;
3627import 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+
4329import 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 ;
4730import org .springframework .data .mongodb .MongoDatabaseFactory ;
4831import org .springframework .data .mongodb .MongoDatabaseUtils ;
4932import org .springframework .data .mongodb .core .convert .ReferenceLoader .DocumentReferenceQuery ;
5033import org .springframework .data .mongodb .core .mapping .BasicMongoPersistentProperty ;
5134import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty ;
5235import org .springframework .lang .Nullable ;
53- import org .springframework .objenesis .ObjenesisStd ;
5436import org .springframework .util .Assert ;
55- import org .springframework .util .ReflectionUtils ;
5637import org .springframework .util .StringUtils ;
5738
5839import com .mongodb .DBRef ;
@@ -74,8 +55,6 @@ public class DefaultDbRefResolver extends DefaultReferenceResolver implements Db
7455 private static final Logger LOGGER = LoggerFactory .getLogger (DefaultDbRefResolver .class );
7556
7657 private final MongoDatabaseFactory mongoDbFactory ;
77- private final PersistenceExceptionTranslator exceptionTranslator ;
78- private final ObjenesisStd objenesis ;
7958
8059 /**
8160 * Creates a new {@link DefaultDbRefResolver} with the given {@link MongoDatabaseFactory}.
@@ -84,13 +63,11 @@ public class DefaultDbRefResolver extends DefaultReferenceResolver implements Db
8463 */
8564 public DefaultDbRefResolver (MongoDatabaseFactory mongoDbFactory ) {
8665
87- super (new MongoDatabaseFactoryReferenceLoader (mongoDbFactory ));
66+ super (new MongoDatabaseFactoryReferenceLoader (mongoDbFactory ), mongoDbFactory . getExceptionTranslator () );
8867
8968 Assert .notNull (mongoDbFactory , "MongoDbFactory translator must not be null!" );
9069
9170 this .mongoDbFactory = mongoDbFactory ;
92- this .exceptionTranslator = mongoDbFactory .getExceptionTranslator ();
93- this .objenesis = new ObjenesisStd (true );
9471 }
9572
9673 /*
@@ -180,44 +157,9 @@ public List<Document> bulkFetch(List<DBRef> refs) {
180157 private Object createLazyLoadingProxy (MongoPersistentProperty property , @ Nullable DBRef dbref ,
181158 DbRefResolverCallback callback , DbRefProxyHandler handler ) {
182159
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 );
203161
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 );
221163 }
222164
223165 /**
@@ -244,249 +186,6 @@ private static Stream<Document> documentWithId(Object identifier, Collection<Doc
244186 .limit (1 );
245187 }
246188
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-
490189 /**
491190 * Customization hook for obtaining the {@link MongoCollection} for a given {@link DBRef}.
492191 *
0 commit comments