Skip to content

Commit dae0ac3

Browse files
committed
Remove duplicate LazyLoadingInterceptor code by reusing LazyLoadingProxyFactory.
Original pull request: #3647. Closes #3602.
1 parent 5ab75eb commit dae0ac3

File tree

9 files changed

+173
-411
lines changed

9 files changed

+173
-411
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java

+4-305
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,25 @@
1515
*/
1616
package 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;
2518
import java.util.ArrayList;
2619
import java.util.Collection;
2720
import java.util.Collections;
2821
import java.util.List;
2922
import java.util.stream.Collectors;
3023
import java.util.stream.Stream;
3124

32-
import org.aopalliance.intercept.MethodInterceptor;
33-
import org.aopalliance.intercept.MethodInvocation;
3425
import org.bson.Document;
3526
import org.slf4j.Logger;
3627
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+
4329
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;
4730
import org.springframework.data.mongodb.MongoDatabaseFactory;
4831
import org.springframework.data.mongodb.MongoDatabaseUtils;
4932
import org.springframework.data.mongodb.core.convert.ReferenceLoader.DocumentReferenceQuery;
5033
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty;
5134
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
5235
import org.springframework.lang.Nullable;
53-
import org.springframework.objenesis.ObjenesisStd;
5436
import org.springframework.util.Assert;
55-
import org.springframework.util.ReflectionUtils;
5637
import org.springframework.util.StringUtils;
5738

5839
import 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

Comments
 (0)