Skip to content

Commit a66438f

Browse files
Resolve cglib proxies during AOT processing.
We now make sure to run the enhancer during AOT which allows the infrastructure to pick up the generated type. Along the lines we removed the no longer supported asserts for class proxies and followed changes in FW6. Closes: #4148
1 parent 0ccc037 commit a66438f

File tree

5 files changed

+79
-94
lines changed

5 files changed

+79
-94
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/LazyLoadingProxyAotProcessor.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.springframework.core.annotation.MergedAnnotations;
3030
import org.springframework.data.annotation.Reference;
3131
import org.springframework.data.aot.TypeUtils;
32+
import org.springframework.data.mongodb.core.convert.LazyLoadingProxyFactory;
33+
import org.springframework.data.mongodb.core.convert.LazyLoadingProxyFactory.LazyLoadingInterceptor;
3234
import org.springframework.data.mongodb.core.mapping.DBRef;
3335
import org.springframework.data.mongodb.core.mapping.DocumentReference;
3436

@@ -72,10 +74,7 @@ void registerLazyLoadingProxyIfNeeded(Class<?> type, GenerationContext generatio
7274

7375
generationContext.getRuntimeHints().proxies().registerJdkProxy(interfaces.toArray(Class[]::new));
7476
} else {
75-
76-
generationContext.getRuntimeHints().proxies().registerClassProxy(field.getType(), builder -> {
77-
builder.proxiedInterfaces(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class);
78-
});
77+
LazyLoadingProxyFactory.resolveProxyType(field.getType(), () -> LazyLoadingInterceptor.none());
7978
}
8079
});
8180
}

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

+74-26
Original file line numberDiff line numberDiff line change
@@ -22,70 +22,75 @@
2222
import java.io.ObjectOutputStream;
2323
import java.io.Serializable;
2424
import java.lang.reflect.Method;
25+
import java.util.function.Supplier;
2526

2627
import org.aopalliance.intercept.MethodInterceptor;
2728
import org.aopalliance.intercept.MethodInvocation;
2829
import org.apache.commons.logging.Log;
2930
import org.apache.commons.logging.LogFactory;
3031
import org.springframework.aop.framework.ProxyFactory;
32+
import org.springframework.cglib.core.SpringNamingPolicy;
3133
import org.springframework.cglib.proxy.Callback;
3234
import org.springframework.cglib.proxy.Enhancer;
3335
import org.springframework.cglib.proxy.Factory;
3436
import org.springframework.cglib.proxy.MethodProxy;
35-
import org.springframework.core.NativeDetector;
3637
import org.springframework.dao.DataAccessException;
3738
import org.springframework.dao.support.PersistenceExceptionTranslator;
3839
import org.springframework.data.mongodb.ClientSessionException;
3940
import org.springframework.data.mongodb.LazyLoadingException;
4041
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
4142
import org.springframework.lang.Nullable;
42-
import org.springframework.objenesis.ObjenesisStd;
43+
import org.springframework.objenesis.SpringObjenesis;
4344
import org.springframework.util.ReflectionUtils;
4445

4546
import com.mongodb.DBRef;
4647

4748
/**
4849
* {@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.
4951
*
5052
* @author Christoph Strobl
5153
* @author Mark Paluch
5254
*/
53-
class LazyLoadingProxyFactory {
55+
public final class LazyLoadingProxyFactory {
5456

5557
private static final Log LOGGER = LogFactory.getLog(LazyLoadingProxyFactory.class);
5658

57-
private final ObjenesisStd objenesis;
59+
private final SpringObjenesis objenesis;
5860

5961
private final PersistenceExceptionTranslator exceptionTranslator;
6062

63+
private LazyLoadingProxyFactory() {
64+
this(ex -> null);
65+
}
66+
6167
public LazyLoadingProxyFactory(PersistenceExceptionTranslator exceptionTranslator) {
6268
this.exceptionTranslator = exceptionTranslator;
63-
this.objenesis = new ObjenesisStd(true);
69+
this.objenesis = new SpringObjenesis(null);
6470
}
6571

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) {
6882

69-
Class<?> propertyType = property.getType();
70-
LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, callback, source, exceptionTranslator);
83+
LazyLoadingProxyFactory factory = new LazyLoadingProxyFactory();
7184

7285
if (!propertyType.isInterface()) {
86+
return factory.getEnhancedTypeFor(propertyType);
87+
}
7388

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+
}
8692

87-
return factory;
88-
}
93+
private ProxyFactory prepareProxyFactory(Class<?> propertyType, Supplier<LazyLoadingInterceptor> interceptor) {
8994

9095
ProxyFactory proxyFactory = new ProxyFactory();
9196

@@ -95,9 +100,27 @@ public Object createLazyLoadingProxy(MongoPersistentProperty property, DbRefReso
95100

96101
proxyFactory.addInterface(LazyLoadingProxy.class);
97102
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()) {
99115

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();
101124
}
102125

103126
/**
@@ -110,8 +133,10 @@ private Class<?> getEnhancedTypeFor(Class<?> type) {
110133

111134
Enhancer enhancer = new Enhancer();
112135
enhancer.setSuperclass(type);
113-
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
136+
enhancer.setCallbackType(LazyLoadingInterceptor.class);
114137
enhancer.setInterfaces(new Class[] { LazyLoadingProxy.class });
138+
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
139+
enhancer.setAttemptLoad(true);
115140

116141
return enhancer.createClass();
117142
}
@@ -139,6 +164,29 @@ public static class LazyLoadingInterceptor
139164
private volatile boolean resolved;
140165
private @Nullable Object result;
141166

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+
142190
public LazyLoadingInterceptor(MongoPersistentProperty property, DbRefResolverCallback callback, Object source,
143191
PersistenceExceptionTranslator exceptionTranslator) {
144192

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/AotMongoRepositoryPostProcessorUnitTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.springframework.data.mongodb.aot.RepositoryRegistrationAotContributionAssert.*;
2020

2121
import org.junit.jupiter.api.Test;
22+
import org.springframework.aot.hint.RuntimeHints;
2223
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
2324
import org.springframework.beans.factory.config.BeanDefinition;
2425
import org.springframework.beans.factory.support.RegisteredBean;
@@ -52,15 +53,14 @@ void contributesProxiesForDataAnnotations() {
5253
contribution.contributesJdkProxy(LastModifiedDate.class, SynthesizedAnnotation.class);
5354
contribution.contributesJdkProxy(Document.class, SynthesizedAnnotation.class);
5455
contribution.contributesJdkProxy(DBRef.class, SynthesizedAnnotation.class);
55-
// TODO: not supported yet contribution.contributesClassProxy(Address.class, LazyLoadingProxy.class);
5656
});
5757
}
5858

5959
BeanContributionBuilder computeConfiguration(Class<?> configuration) {
6060

6161
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
6262
ctx.register(configuration);
63-
ctx.refreshForAotProcessing();
63+
ctx.refreshForAotProcessing(new RuntimeHints());
6464

6565
return it -> {
6666

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/ClassProxyAssert.java

-45
This file was deleted.

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/aot/CodeContributionAssert.java

-17
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
import org.assertj.core.api.AbstractAssert;
2424
import org.springframework.aot.generate.GenerationContext;
25-
import org.springframework.aot.hint.ClassProxyHint;
2625
import org.springframework.aot.hint.JdkProxyHint;
2726
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
2827

@@ -101,20 +100,4 @@ private Stream<JdkProxyHint> jdkProxiesFor(Class<?> entryPoint) {
101100
.filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName()
102101
.equals(entryPoint.getCanonicalName()));
103102
}
104-
105-
public CodeContributionAssert contributesClassProxy(Class<?>... proxyInterfaces) {
106-
107-
assertThat(classProxiesFor(proxyInterfaces[0]))
108-
.describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces))
109-
.anySatisfy(it -> new ClassProxyAssert(it).matches(proxyInterfaces));
110-
111-
return this;
112-
}
113-
114-
private Stream<ClassProxyHint> classProxiesFor(Class<?> entryPoint) {
115-
116-
return this.actual.getRuntimeHints().proxies().classProxies()
117-
.filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName()
118-
.equals(entryPoint.getCanonicalName()));
119-
}
120103
}

0 commit comments

Comments
 (0)