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
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.core.SmartClassLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -33,7 +34,8 @@ import org.springframework.lang.Nullable;
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
|
||||
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport
|
||||
implements SmartInstantiationAwareBeanPostProcessor {
|
||||
|
||||
@Nullable
|
||||
protected Advisor advisor;
|
||||
@@ -58,8 +60,27 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
|
||||
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
public Class<?> determineBeanType(Class<?> beanClass, String beanName) {
|
||||
if (this.advisor != null && isEligible(beanClass)) {
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
proxyFactory.copyFrom(this);
|
||||
proxyFactory.setTargetClass(beanClass);
|
||||
|
||||
if (!proxyFactory.isProxyTargetClass()) {
|
||||
evaluateProxyInterfaces(beanClass, proxyFactory);
|
||||
}
|
||||
proxyFactory.addAdvisor(this.advisor);
|
||||
customizeProxyFactory(proxyFactory);
|
||||
|
||||
// Use original ClassLoader if bean class not locally loaded in overriding class loader
|
||||
ClassLoader classLoader = getProxyClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
}
|
||||
return proxyFactory.getProxyClass(classLoader);
|
||||
}
|
||||
|
||||
return beanClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,4 +52,13 @@ public interface AopProxy {
|
||||
*/
|
||||
Object getProxy(@Nullable ClassLoader classLoader);
|
||||
|
||||
/**
|
||||
* Determine the proxy class.
|
||||
* @param classLoader the class loader to create the proxy class with
|
||||
* (or {@code null} for the low-level proxy facility's default)
|
||||
* @return the proxy class
|
||||
* @since 6.0
|
||||
*/
|
||||
Class<?> getProxyClass(@Nullable ClassLoader classLoader);
|
||||
|
||||
}
|
||||
|
||||
@@ -153,11 +153,20 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
|
||||
@Override
|
||||
public Object getProxy() {
|
||||
return getProxy(null);
|
||||
return buildProxy(null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProxy(@Nullable ClassLoader classLoader) {
|
||||
return buildProxy(classLoader, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
|
||||
return (Class<?>) buildProxy(classLoader, true);
|
||||
}
|
||||
|
||||
private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
|
||||
}
|
||||
@@ -190,6 +199,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
enhancer.setSuperclass(proxySuperClass);
|
||||
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
|
||||
|
||||
Callback[] callbacks = getCallbacks(rootClass);
|
||||
@@ -203,7 +213,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
enhancer.setCallbackTypes(types);
|
||||
|
||||
// Generate the proxy class and create a proxy instance.
|
||||
return createProxyClassAndInstance(enhancer, callbacks);
|
||||
return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
|
||||
}
|
||||
catch (CodeGenerationException | IllegalArgumentException ex) {
|
||||
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
|
||||
@@ -216,6 +226,11 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
protected Class<?> createProxyClass(Enhancer enhancer) {
|
||||
enhancer.setInterceptDuringConstruction(false);
|
||||
return enhancer.createClass();
|
||||
}
|
||||
|
||||
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
|
||||
enhancer.setInterceptDuringConstruction(false);
|
||||
enhancer.setCallbacks(callbacks);
|
||||
@@ -375,22 +390,6 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given method with a CGLIB MethodProxy if possible, falling back
|
||||
* to a plain reflection invocation in case of a fast-class generation failure.
|
||||
*/
|
||||
@Nullable
|
||||
private static Object invokeMethod(@Nullable Object target, Method method, Object[] args, MethodProxy methodProxy)
|
||||
throws Throwable {
|
||||
try {
|
||||
return methodProxy.invoke(target, args);
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
CglibMethodInvocation.logFastClassGenerationFailure(method);
|
||||
return AopUtils.invokeJoinpointUsingReflection(target, method, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a return value. Wraps a return of {@code this} if necessary to be the
|
||||
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
|
||||
@@ -424,10 +423,9 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* Method interceptor used for static targets with no advice chain. The call
|
||||
* is passed directly back to the target. Used when the proxy needs to be
|
||||
* exposed and it can't be determined that the method won't return
|
||||
* {@code this}.
|
||||
* Method interceptor used for static targets with no advice chain. The call is
|
||||
* passed directly back to the target. Used when the proxy needs to be exposed
|
||||
* and it can't be determined that the method won't return {@code this}.
|
||||
*/
|
||||
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
|
||||
|
||||
@@ -441,7 +439,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
@Override
|
||||
@Nullable
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object retVal = invokeMethod(this.target, method, args, methodProxy);
|
||||
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.target, method, args);
|
||||
return processReturnType(proxy, this.target, method, retVal);
|
||||
}
|
||||
}
|
||||
@@ -466,7 +464,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Object oldProxy = null;
|
||||
try {
|
||||
oldProxy = AopContext.setCurrentProxy(proxy);
|
||||
Object retVal = invokeMethod(this.target, method, args, methodProxy);
|
||||
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.target, method, args);
|
||||
return processReturnType(proxy, this.target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
@@ -494,7 +492,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object target = this.targetSource.getTarget();
|
||||
try {
|
||||
Object retVal = invokeMethod(target, method, args, methodProxy);
|
||||
Object retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
|
||||
return processReturnType(proxy, target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
@@ -524,7 +522,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Object target = this.targetSource.getTarget();
|
||||
try {
|
||||
oldProxy = AopContext.setCurrentProxy(proxy);
|
||||
Object retVal = invokeMethod(target, method, args, methodProxy);
|
||||
Object retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
|
||||
return processReturnType(proxy, target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
@@ -695,13 +693,13 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Object retVal;
|
||||
// Check whether we only have one InvokerInterceptor: that is,
|
||||
// no real advice, but just reflective invocation of the target.
|
||||
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
|
||||
if (chain.isEmpty()) {
|
||||
// We can skip creating a MethodInvocation: just invoke the target directly.
|
||||
// Note that the final invoker must be an InvokerInterceptor, so we know
|
||||
// it does nothing but a reflective operation on the target, and no hot
|
||||
// swapping or fancy proxying.
|
||||
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
|
||||
retVal = invokeMethod(target, method, argsToUse, methodProxy);
|
||||
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
|
||||
}
|
||||
else {
|
||||
// We need to create a method invocation...
|
||||
@@ -743,17 +741,11 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
*/
|
||||
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
|
||||
|
||||
@Nullable
|
||||
private final MethodProxy methodProxy;
|
||||
|
||||
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
|
||||
Object[] arguments, @Nullable Class<?> targetClass,
|
||||
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
|
||||
|
||||
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
|
||||
|
||||
// Only use method proxy for public methods not derived from java.lang.Object
|
||||
this.methodProxy = (isMethodProxyCompatible(method) ? methodProxy : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -781,35 +773,6 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives a marginal performance improvement versus using reflection to
|
||||
* invoke the target when invoking public methods.
|
||||
*/
|
||||
@Override
|
||||
protected Object invokeJoinpoint() throws Throwable {
|
||||
if (this.methodProxy != null) {
|
||||
try {
|
||||
return this.methodProxy.invoke(this.target, this.arguments);
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
logFastClassGenerationFailure(this.method);
|
||||
}
|
||||
}
|
||||
return super.invokeJoinpoint();
|
||||
}
|
||||
|
||||
static boolean isMethodProxyCompatible(Method method) {
|
||||
return (Modifier.isPublic(method.getModifiers()) &&
|
||||
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
|
||||
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method));
|
||||
}
|
||||
|
||||
static void logFastClassGenerationFailure(Method method) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to generate CGLIB fast class for method: " + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.io.Serializable;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import org.springframework.aop.SpringProxy;
|
||||
import org.springframework.core.NativeDetector;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -63,9 +62,6 @@ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
|
||||
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
|
||||
return new JdkDynamicAopProxy(config);
|
||||
}
|
||||
if (NativeDetector.inNativeImage()) {
|
||||
throw new AopConfigException("Subclass-based proxies are not support yet in native images");
|
||||
}
|
||||
return new ObjenesisCglibAopProxy(config);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -126,6 +126,12 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
|
||||
return Proxy.getProxyClass(classLoader, this.proxiedInterfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds any {@link #equals} or {@link #hashCode} method that may be defined
|
||||
* on the supplied set of interfaces.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,6 +52,11 @@ class ObjenesisCglibAopProxy extends CglibAopProxy {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Class<?> createProxyClass(Enhancer enhancer) {
|
||||
return enhancer.createClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
|
||||
Class<?> proxyClass = enhancer.createClass();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -110,6 +110,17 @@ public class ProxyFactory extends ProxyCreatorSupport {
|
||||
return createAopProxy().getProxy(classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the proxy class according to the settings in this factory.
|
||||
* @param classLoader the class loader to create the proxy class with
|
||||
* (or {@code null} for the low-level proxy facility's default)
|
||||
* @return the proxy class
|
||||
* @since 6.0
|
||||
*/
|
||||
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
|
||||
return createAopProxy().getProxyClass(classLoader);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new proxy for the given interface and interceptor.
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.framework.ProxyProcessorSupport;
|
||||
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
|
||||
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
@@ -231,6 +232,30 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
return this.proxyTypes.get(cacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> determineBeanType(Class<?> beanClass, String beanName) {
|
||||
Object cacheKey = getCacheKey(beanClass, beanName);
|
||||
Class<?> proxyType = this.proxyTypes.get(cacheKey);
|
||||
if (proxyType == null) {
|
||||
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
|
||||
if (targetSource != null) {
|
||||
if (StringUtils.hasLength(beanName)) {
|
||||
this.targetSourcedBeans.add(beanName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
targetSource = EmptyTargetSource.forClass(beanClass);
|
||||
}
|
||||
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
|
||||
if (specificInterceptors != DO_NOT_PROXY) {
|
||||
this.advisedBeans.put(cacheKey, Boolean.TRUE);
|
||||
proxyType = createProxyClass(beanClass, beanName, specificInterceptors, targetSource);
|
||||
this.proxyTypes.put(cacheKey, proxyType);
|
||||
}
|
||||
}
|
||||
return (proxyType != null ? proxyType : beanClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) {
|
||||
@@ -436,6 +461,18 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
|
||||
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
|
||||
|
||||
return buildProxy(beanClass, beanName, specificInterceptors, targetSource, false);
|
||||
}
|
||||
|
||||
private Class<?> createProxyClass(Class<?> beanClass, @Nullable String beanName,
|
||||
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
|
||||
|
||||
return (Class<?>) buildProxy(beanClass, beanName, specificInterceptors, targetSource, true);
|
||||
}
|
||||
|
||||
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
|
||||
@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
|
||||
|
||||
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
|
||||
}
|
||||
@@ -477,7 +514,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
}
|
||||
return proxyFactory.getProxy(classLoader);
|
||||
return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user