Skip to content

Commit b31a158

Browse files
committed
Support for pre-generated CGLIB proxy classes (in AOT scenarios)
Includes runtime storing of generated classes to a directory specified by the "cglib.generatedClasses" system property. Avoids lazy CGLIB fast-class generation and replaces generated Enhancer and MethodWrapper key classes with equivalent record types. Introduces support for early type determination in InstantiationStrategy, AopProxy and SmartInstantiationAwareBeanPostProcessor - in order to trigger CGLIB class generation in refreshForAotProcessing (through early determineBeanType calls for bean definitions). Closes gh-28115
1 parent 496b187 commit b31a158

File tree

25 files changed

+364
-173
lines changed

25 files changed

+364
-173
lines changed

spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import org.springframework.aop.Advisor;
2323
import org.springframework.aop.support.AopUtils;
2424
import org.springframework.beans.factory.config.BeanPostProcessor;
25+
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
2526
import org.springframework.core.SmartClassLoader;
2627
import org.springframework.lang.Nullable;
2728

@@ -33,7 +34,8 @@
3334
* @since 3.2
3435
*/
3536
@SuppressWarnings("serial")
36-
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
37+
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport
38+
implements SmartInstantiationAwareBeanPostProcessor {
3739

3840
@Nullable
3941
protected Advisor advisor;
@@ -58,8 +60,27 @@ public void setBeforeExistingAdvisors(boolean beforeExistingAdvisors) {
5860

5961

6062
@Override
61-
public Object postProcessBeforeInitialization(Object bean, String beanName) {
62-
return bean;
63+
public Class<?> determineBeanType(Class<?> beanClass, String beanName) {
64+
if (this.advisor != null && isEligible(beanClass)) {
65+
ProxyFactory proxyFactory = new ProxyFactory();
66+
proxyFactory.copyFrom(this);
67+
proxyFactory.setTargetClass(beanClass);
68+
69+
if (!proxyFactory.isProxyTargetClass()) {
70+
evaluateProxyInterfaces(beanClass, proxyFactory);
71+
}
72+
proxyFactory.addAdvisor(this.advisor);
73+
customizeProxyFactory(proxyFactory);
74+
75+
// Use original ClassLoader if bean class not locally loaded in overriding class loader
76+
ClassLoader classLoader = getProxyClassLoader();
77+
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
78+
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
79+
}
80+
return proxyFactory.getProxyClass(classLoader);
81+
}
82+
83+
return beanClass;
6384
}
6485

6586
@Override

spring-aop/src/main/java/org/springframework/aop/framework/AopProxy.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,4 +52,13 @@ public interface AopProxy {
5252
*/
5353
Object getProxy(@Nullable ClassLoader classLoader);
5454

55+
/**
56+
* Determine the proxy class.
57+
* @param classLoader the class loader to create the proxy class with
58+
* (or {@code null} for the low-level proxy facility's default)
59+
* @return the proxy class
60+
* @since 6.0
61+
*/
62+
Class<?> getProxyClass(@Nullable ClassLoader classLoader);
63+
5564
}

spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java

Lines changed: 26 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,20 @@ public void setConstructorArguments(@Nullable Object[] constructorArgs, @Nullabl
153153

154154
@Override
155155
public Object getProxy() {
156-
return getProxy(null);
156+
return buildProxy(null, false);
157157
}
158158

159159
@Override
160160
public Object getProxy(@Nullable ClassLoader classLoader) {
161+
return buildProxy(classLoader, false);
162+
}
163+
164+
@Override
165+
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
166+
return (Class<?>) buildProxy(classLoader, true);
167+
}
168+
169+
private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {
161170
if (logger.isTraceEnabled()) {
162171
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
163172
}
@@ -190,6 +199,7 @@ public Object getProxy(@Nullable ClassLoader classLoader) {
190199
enhancer.setSuperclass(proxySuperClass);
191200
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
192201
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
202+
enhancer.setAttemptLoad(true);
193203
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
194204

195205
Callback[] callbacks = getCallbacks(rootClass);
@@ -203,7 +213,7 @@ public Object getProxy(@Nullable ClassLoader classLoader) {
203213
enhancer.setCallbackTypes(types);
204214

205215
// Generate the proxy class and create a proxy instance.
206-
return createProxyClassAndInstance(enhancer, callbacks);
216+
return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
207217
}
208218
catch (CodeGenerationException | IllegalArgumentException ex) {
209219
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
@@ -216,6 +226,11 @@ public Object getProxy(@Nullable ClassLoader classLoader) {
216226
}
217227
}
218228

229+
protected Class<?> createProxyClass(Enhancer enhancer) {
230+
enhancer.setInterceptDuringConstruction(false);
231+
return enhancer.createClass();
232+
}
233+
219234
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
220235
enhancer.setInterceptDuringConstruction(false);
221236
enhancer.setCallbacks(callbacks);
@@ -375,22 +390,6 @@ private static boolean implementsInterface(Method method, Set<Class<?>> ifcs) {
375390
return false;
376391
}
377392

378-
/**
379-
* Invoke the given method with a CGLIB MethodProxy if possible, falling back
380-
* to a plain reflection invocation in case of a fast-class generation failure.
381-
*/
382-
@Nullable
383-
private static Object invokeMethod(@Nullable Object target, Method method, Object[] args, MethodProxy methodProxy)
384-
throws Throwable {
385-
try {
386-
return methodProxy.invoke(target, args);
387-
}
388-
catch (CodeGenerationException ex) {
389-
CglibMethodInvocation.logFastClassGenerationFailure(method);
390-
return AopUtils.invokeJoinpointUsingReflection(target, method, args);
391-
}
392-
}
393-
394393
/**
395394
* Process a return value. Wraps a return of {@code this} if necessary to be the
396395
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
@@ -424,10 +423,9 @@ public static class SerializableNoOp implements NoOp, Serializable {
424423

425424

426425
/**
427-
* Method interceptor used for static targets with no advice chain. The call
428-
* is passed directly back to the target. Used when the proxy needs to be
429-
* exposed and it can't be determined that the method won't return
430-
* {@code this}.
426+
* Method interceptor used for static targets with no advice chain. The call is
427+
* passed directly back to the target. Used when the proxy needs to be exposed
428+
* and it can't be determined that the method won't return {@code this}.
431429
*/
432430
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
433431

@@ -441,7 +439,7 @@ public StaticUnadvisedInterceptor(@Nullable Object target) {
441439
@Override
442440
@Nullable
443441
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
444-
Object retVal = invokeMethod(this.target, method, args, methodProxy);
442+
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.target, method, args);
445443
return processReturnType(proxy, this.target, method, retVal);
446444
}
447445
}
@@ -466,7 +464,7 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy
466464
Object oldProxy = null;
467465
try {
468466
oldProxy = AopContext.setCurrentProxy(proxy);
469-
Object retVal = invokeMethod(this.target, method, args, methodProxy);
467+
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.target, method, args);
470468
return processReturnType(proxy, this.target, method, retVal);
471469
}
472470
finally {
@@ -494,7 +492,7 @@ public DynamicUnadvisedInterceptor(TargetSource targetSource) {
494492
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
495493
Object target = this.targetSource.getTarget();
496494
try {
497-
Object retVal = invokeMethod(target, method, args, methodProxy);
495+
Object retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
498496
return processReturnType(proxy, target, method, retVal);
499497
}
500498
finally {
@@ -524,7 +522,7 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy
524522
Object target = this.targetSource.getTarget();
525523
try {
526524
oldProxy = AopContext.setCurrentProxy(proxy);
527-
Object retVal = invokeMethod(target, method, args, methodProxy);
525+
Object retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
528526
return processReturnType(proxy, target, method, retVal);
529527
}
530528
finally {
@@ -695,13 +693,13 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy
695693
Object retVal;
696694
// Check whether we only have one InvokerInterceptor: that is,
697695
// no real advice, but just reflective invocation of the target.
698-
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
696+
if (chain.isEmpty()) {
699697
// We can skip creating a MethodInvocation: just invoke the target directly.
700698
// Note that the final invoker must be an InvokerInterceptor, so we know
701699
// it does nothing but a reflective operation on the target, and no hot
702700
// swapping or fancy proxying.
703701
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
704-
retVal = invokeMethod(target, method, argsToUse, methodProxy);
702+
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
705703
}
706704
else {
707705
// We need to create a method invocation...
@@ -743,17 +741,11 @@ public int hashCode() {
743741
*/
744742
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
745743

746-
@Nullable
747-
private final MethodProxy methodProxy;
748-
749744
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
750745
Object[] arguments, @Nullable Class<?> targetClass,
751746
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
752747

753748
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
754-
755-
// Only use method proxy for public methods not derived from java.lang.Object
756-
this.methodProxy = (isMethodProxyCompatible(method) ? methodProxy : null);
757749
}
758750

759751
@Override
@@ -781,35 +773,6 @@ public Object proceed() throws Throwable {
781773
}
782774
}
783775
}
784-
785-
/**
786-
* Gives a marginal performance improvement versus using reflection to
787-
* invoke the target when invoking public methods.
788-
*/
789-
@Override
790-
protected Object invokeJoinpoint() throws Throwable {
791-
if (this.methodProxy != null) {
792-
try {
793-
return this.methodProxy.invoke(this.target, this.arguments);
794-
}
795-
catch (CodeGenerationException ex) {
796-
logFastClassGenerationFailure(this.method);
797-
}
798-
}
799-
return super.invokeJoinpoint();
800-
}
801-
802-
static boolean isMethodProxyCompatible(Method method) {
803-
return (Modifier.isPublic(method.getModifiers()) &&
804-
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
805-
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method));
806-
}
807-
808-
static void logFastClassGenerationFailure(Method method) {
809-
if (logger.isDebugEnabled()) {
810-
logger.debug("Failed to generate CGLIB fast class for method: " + method);
811-
}
812-
}
813776
}
814777

815778

spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.lang.reflect.Proxy;
2121

2222
import org.springframework.aop.SpringProxy;
23-
import org.springframework.core.NativeDetector;
2423
import org.springframework.util.ClassUtils;
2524

2625
/**
@@ -63,9 +62,6 @@ public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException
6362
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
6463
return new JdkDynamicAopProxy(config);
6564
}
66-
if (NativeDetector.inNativeImage()) {
67-
throw new AopConfigException("Subclass-based proxies are not support yet in native images");
68-
}
6965
return new ObjenesisCglibAopProxy(config);
7066
}
7167
else {

spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -126,6 +126,12 @@ public Object getProxy(@Nullable ClassLoader classLoader) {
126126
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
127127
}
128128

129+
@SuppressWarnings("deprecation")
130+
@Override
131+
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
132+
return Proxy.getProxyClass(classLoader, this.proxiedInterfaces);
133+
}
134+
129135
/**
130136
* Finds any {@link #equals} or {@link #hashCode} method that may be defined
131137
* on the supplied set of interfaces.

spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,6 +52,11 @@ public ObjenesisCglibAopProxy(AdvisedSupport config) {
5252
}
5353

5454

55+
@Override
56+
protected Class<?> createProxyClass(Enhancer enhancer) {
57+
return enhancer.createClass();
58+
}
59+
5560
@Override
5661
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
5762
Class<?> proxyClass = enhancer.createClass();

spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -110,6 +110,17 @@ public Object getProxy(@Nullable ClassLoader classLoader) {
110110
return createAopProxy().getProxy(classLoader);
111111
}
112112

113+
/**
114+
* Determine the proxy class according to the settings in this factory.
115+
* @param classLoader the class loader to create the proxy class with
116+
* (or {@code null} for the low-level proxy facility's default)
117+
* @return the proxy class
118+
* @since 6.0
119+
*/
120+
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
121+
return createAopProxy().getProxyClass(classLoader);
122+
}
123+
113124

114125
/**
115126
* Create a new proxy for the given interface and interceptor.

0 commit comments

Comments
 (0)