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:
parent
496b1879ab
commit
b31a15851e
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -69,6 +69,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
|||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.support.LookupOverride;
|
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
|
@ -311,44 +312,26 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
this.injectionMetadataCache.remove(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> determineBeanType(Class<?> beanClass, String beanName) throws BeanCreationException {
|
||||
checkLookupMethods(beanClass, beanName);
|
||||
|
||||
// Pick up subclass with fresh lookup method override from above
|
||||
if (this.beanFactory instanceof AbstractAutowireCapableBeanFactory aacbf) {
|
||||
RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName);
|
||||
if (mbd.getFactoryMethodName() == null && mbd.hasBeanClass()) {
|
||||
return aacbf.getInstantiationStrategy().getActualBeanClass(mbd, beanName, this.beanFactory);
|
||||
}
|
||||
}
|
||||
return beanClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
|
||||
throws BeanCreationException {
|
||||
|
||||
// Let's check for lookup methods here...
|
||||
if (!this.lookupMethodsChecked.contains(beanName)) {
|
||||
if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
|
||||
try {
|
||||
Class<?> targetClass = beanClass;
|
||||
do {
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
Lookup lookup = method.getAnnotation(Lookup.class);
|
||||
if (lookup != null) {
|
||||
Assert.state(this.beanFactory != null, "No BeanFactory available");
|
||||
LookupOverride override = new LookupOverride(method, lookup.value());
|
||||
try {
|
||||
RootBeanDefinition mbd = (RootBeanDefinition)
|
||||
this.beanFactory.getMergedBeanDefinition(beanName);
|
||||
mbd.getMethodOverrides().addOverride(override);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new BeanCreationException(beanName,
|
||||
"Cannot apply @Lookup to beans without corresponding bean definition");
|
||||
}
|
||||
}
|
||||
});
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
|
||||
}
|
||||
}
|
||||
this.lookupMethodsChecked.add(beanName);
|
||||
}
|
||||
checkLookupMethods(beanClass, beanName);
|
||||
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
|
||||
|
@ -450,6 +433,41 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
return (candidateConstructors.length > 0 ? candidateConstructors : null);
|
||||
}
|
||||
|
||||
private void checkLookupMethods(Class<?> beanClass, final String beanName) throws BeanCreationException {
|
||||
if (!this.lookupMethodsChecked.contains(beanName)) {
|
||||
if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
|
||||
try {
|
||||
Class<?> targetClass = beanClass;
|
||||
do {
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
Lookup lookup = method.getAnnotation(Lookup.class);
|
||||
if (lookup != null) {
|
||||
Assert.state(this.beanFactory != null, "No BeanFactory available");
|
||||
LookupOverride override = new LookupOverride(method, lookup.value());
|
||||
try {
|
||||
RootBeanDefinition mbd = (RootBeanDefinition)
|
||||
this.beanFactory.getMergedBeanDefinition(beanName);
|
||||
mbd.getMethodOverrides().addOverride(override);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new BeanCreationException(beanName,
|
||||
"Cannot apply @Lookup to beans without corresponding bean definition");
|
||||
}
|
||||
}
|
||||
});
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
|
||||
}
|
||||
}
|
||||
this.lookupMethodsChecked.add(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
|
||||
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
|
||||
|
|
|
@ -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.
|
||||
|
@ -28,12 +28,10 @@ import org.springframework.lang.Nullable;
|
|||
* <p><b>NOTE:</b> This interface is a special purpose interface, mainly for
|
||||
* internal use within the framework. In general, application-provided
|
||||
* post-processors should simply implement the plain {@link BeanPostProcessor}
|
||||
* interface or derive from the {@link InstantiationAwareBeanPostProcessorAdapter}
|
||||
* class. New methods might be added to this interface even in point releases.
|
||||
* interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.3
|
||||
* @see InstantiationAwareBeanPostProcessorAdapter
|
||||
*/
|
||||
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
|
||||
|
||||
|
@ -41,6 +39,8 @@ public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationA
|
|||
* Predict the type of the bean to be eventually returned from this
|
||||
* processor's {@link #postProcessBeforeInstantiation} callback.
|
||||
* <p>The default implementation returns {@code null}.
|
||||
* Specific implementations should try to predict the bean type as
|
||||
* far as known/cached already, without extra processing steps.
|
||||
* @param beanClass the raw class of the bean
|
||||
* @param beanName the name of the bean
|
||||
* @return the type of the bean, or {@code null} if not predictable
|
||||
|
@ -51,6 +51,22 @@ public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationA
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of the bean to be eventually returned from this
|
||||
* processor's {@link #postProcessBeforeInstantiation} callback.
|
||||
* <p>The default implementation returns the given bean class as-is.
|
||||
* Specific implementations should fully evaluate their processing steps
|
||||
* in order to create/initialize a potential proxy class upfront.
|
||||
* @param beanClass the raw class of the bean
|
||||
* @param beanName the name of the bean
|
||||
* @return the type of the bean (never {@code null})
|
||||
* @throws org.springframework.beans.BeansException in case of errors
|
||||
* @since 6.0
|
||||
*/
|
||||
default Class<?> determineBeanType(Class<?> beanClass, String beanName) throws BeansException {
|
||||
return beanClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the candidate constructors to use for the given bean.
|
||||
* <p>The default implementation returns {@code null}.
|
||||
|
|
|
@ -70,7 +70,6 @@ import org.springframework.beans.factory.config.TypedStringValue;
|
|||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.core.NativeDetector;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
@ -174,13 +173,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
ignoreDependencyInterface(BeanNameAware.class);
|
||||
ignoreDependencyInterface(BeanFactoryAware.class);
|
||||
ignoreDependencyInterface(BeanClassLoaderAware.class);
|
||||
if (NativeDetector.inNativeImage()) {
|
||||
this.instantiationStrategy = new SimpleInstantiationStrategy();
|
||||
}
|
||||
else {
|
||||
this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AbstractAutowireCapableBeanFactory with the given parent.
|
||||
|
@ -204,7 +198,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
/**
|
||||
* Return the instantiation strategy to use for creating bean instances.
|
||||
*/
|
||||
protected InstantiationStrategy getInstantiationStrategy() {
|
||||
public InstantiationStrategy getInstantiationStrategy() {
|
||||
return this.instantiationStrategy;
|
||||
}
|
||||
|
||||
|
@ -222,7 +216,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* names if needed.
|
||||
*/
|
||||
@Nullable
|
||||
protected ParameterNameDiscoverer getParameterNameDiscoverer() {
|
||||
public ParameterNameDiscoverer getParameterNameDiscoverer() {
|
||||
return this.parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
|
@ -683,9 +677,15 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
|
||||
Class<?> targetType = mbd.getTargetType();
|
||||
if (targetType == null) {
|
||||
targetType = (mbd.getFactoryMethodName() != null ?
|
||||
getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
|
||||
resolveBeanClass(mbd, beanName, typesToMatch));
|
||||
if (mbd.getFactoryMethodName() != null) {
|
||||
targetType = getTypeForFactoryMethod(beanName, mbd, typesToMatch);
|
||||
}
|
||||
else {
|
||||
targetType = resolveBeanClass(mbd, beanName, typesToMatch);
|
||||
if (mbd.hasBeanClass()) {
|
||||
targetType = getInstantiationStrategy().getActualBeanClass(mbd, beanName, this);
|
||||
}
|
||||
}
|
||||
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
|
||||
mbd.resolvedTargetType = targetType;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -81,10 +81,17 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
|||
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
|
||||
@Nullable Constructor<?> ctor, Object... args) {
|
||||
|
||||
// Must generate CGLIB subclass...
|
||||
return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getActualBeanClass(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
|
||||
if (!bd.hasMethodOverrides()) {
|
||||
return super.getActualBeanClass(bd, beanName, owner);
|
||||
}
|
||||
return new CglibSubclassCreator(bd, owner).createEnhancedSubclass(bd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An inner class created for historical reasons to avoid external CGLIB dependency
|
||||
|
@ -142,10 +149,11 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
|||
* Create an enhanced subclass of the bean class for the provided bean
|
||||
* definition, using CGLIB.
|
||||
*/
|
||||
private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
|
||||
public Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(beanDefinition.getBeanClass());
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
if (this.owner instanceof ConfigurableBeanFactory) {
|
||||
ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
|
||||
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
|
@ -83,4 +83,12 @@ public interface InstantiationStrategy {
|
|||
@Nullable Object factoryBean, Method factoryMethod, Object... args)
|
||||
throws BeansException;
|
||||
|
||||
/**
|
||||
* Determine the actual class for the given bean definition, as instantiated at runtime.
|
||||
* @since 6.0
|
||||
*/
|
||||
default Class<?> getActualBeanClass(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
|
||||
return bd.getBeanClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -422,6 +422,11 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
|||
boolean traceEnabled = logger.isTraceEnabled();
|
||||
boolean debugEnabled = logger.isDebugEnabled();
|
||||
for (Resource resource : resources) {
|
||||
String filename = resource.getFilename();
|
||||
if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
|
||||
// Ignore CGLIB-generated classes in the classpath
|
||||
continue;
|
||||
}
|
||||
if (traceEnabled) {
|
||||
logger.trace("Scanning " + resource);
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ class ConfigurationClassEnhancer {
|
|||
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
|
||||
enhancer.setUseFactory(false);
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
|
||||
enhancer.setCallbackFilter(CALLBACK_FILTER);
|
||||
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
|
||||
|
@ -508,6 +509,7 @@ class ConfigurationClassEnhancer {
|
|||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(factoryBean.getClass());
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setCallbackType(MethodInterceptor.class);
|
||||
|
||||
// Ideally create enhanced FactoryBean proxy without constructor side effects,
|
||||
|
|
|
@ -64,7 +64,6 @@ import org.springframework.context.ApplicationStartupAware;
|
|||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
|
||||
import org.springframework.core.NativeDetector;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
@ -286,11 +285,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
}
|
||||
|
||||
@Override
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
return (beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME)
|
||||
? new AotContribution(beanFactory) : null);
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
return (beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME) ? new AotContribution(beanFactory) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -452,7 +448,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
configBeanDefs.put(beanName, abd);
|
||||
}
|
||||
}
|
||||
if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {
|
||||
if (configBeanDefs.isEmpty()) {
|
||||
// nothing to enhance -> return immediately
|
||||
enhanceConfigClasses.end();
|
||||
return;
|
||||
|
@ -514,8 +510,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
|
||||
private static final String BEAN_FACTORY_VARIABLE = BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE;
|
||||
|
||||
private static final ParameterizedTypeName STRING_STRING_MAP = ParameterizedTypeName
|
||||
.get(Map.class, String.class, String.class);
|
||||
private static final ParameterizedTypeName STRING_STRING_MAP =
|
||||
ParameterizedTypeName.get(Map.class, String.class, String.class);
|
||||
|
||||
private static final String MAPPINGS_VARIABLE = "mappings";
|
||||
|
||||
|
@ -523,15 +519,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
|
||||
private static final String BEAN_NAME = "org.springframework.context.annotation.internalImportAwareAotProcessor";
|
||||
|
||||
|
||||
private final ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
public AotContribution(ConfigurableListableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext,
|
||||
BeanFactoryInitializationCode beanFactoryInitializationCode) {
|
||||
|
@ -550,11 +543,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
}
|
||||
}
|
||||
|
||||
private void generateAddPostProcessorMethod(MethodSpec.Builder method,
|
||||
Map<String, String> mappings) {
|
||||
|
||||
method.addJavadoc(
|
||||
"Add ImportAwareBeanPostProcessor to support ImportAware beans");
|
||||
private void generateAddPostProcessorMethod(MethodSpec.Builder method, Map<String, String> mappings) {
|
||||
method.addJavadoc("Add ImportAwareBeanPostProcessor to support ImportAware beans");
|
||||
method.addModifiers(Modifier.PRIVATE);
|
||||
method.addParameter(DefaultListableBeanFactory.class, BEAN_FACTORY_VARIABLE);
|
||||
method.addCode(generateAddPostProcessorCode(mappings));
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.context.support;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -29,6 +30,7 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
|
@ -408,8 +410,29 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
|
|||
invokeBeanFactoryPostProcessors(this.beanFactory);
|
||||
this.beanFactory.freezeConfiguration();
|
||||
PostProcessorRegistrationDelegate.invokeMergedBeanDefinitionPostProcessors(this.beanFactory);
|
||||
preDetermineBeanTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-determine bean types in order to trigger early proxy class creation.
|
||||
* @see org.springframework.beans.factory.BeanFactory#getType
|
||||
* @see SmartInstantiationAwareBeanPostProcessor#determineBeanType
|
||||
*/
|
||||
private void preDetermineBeanTypes() {
|
||||
List<SmartInstantiationAwareBeanPostProcessor> bpps =
|
||||
PostProcessorRegistrationDelegate.loadBeanPostProcessors(
|
||||
this.beanFactory, SmartInstantiationAwareBeanPostProcessor.class);
|
||||
for (String beanName : this.beanFactory.getBeanDefinitionNames()) {
|
||||
Class<?> beanType = this.beanFactory.getType(beanName);
|
||||
if (beanType != null) {
|
||||
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
|
||||
beanType = bpp.determineBeanType(beanType, beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Convenient methods for registering individual beans
|
||||
//---------------------------------------------------------------------
|
||||
|
|
|
@ -135,6 +135,7 @@ jar {
|
|||
exclude "org/springframework/cglib/core/AsmApi*.class"
|
||||
exclude "org/springframework/cglib/core/KeyFactory.class"
|
||||
exclude "org/springframework/cglib/core/KeyFactory\$*.class"
|
||||
exclude "org/springframework/cglib/core/MethodWrapper*.class"
|
||||
exclude "org/springframework/cglib/core/ReflectUtils*.class"
|
||||
exclude "org/springframework/cglib/proxy/Enhancer*.class"
|
||||
exclude "org/springframework/cglib/proxy/MethodProxy*.class"
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2003,2004 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cglib.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public class MethodWrapper {
|
||||
|
||||
// SPRING PATCH BEGIN
|
||||
private record MethodWrapperKey(String name, List<String> parameterTypes, String returnType) {
|
||||
}
|
||||
// SPRING PATCH END
|
||||
|
||||
private MethodWrapper() {
|
||||
}
|
||||
|
||||
public static Object create(Method method) {
|
||||
// SPRING PATCH BEGIN
|
||||
return new MethodWrapperKey(method.getName(),
|
||||
Arrays.asList(ReflectUtils.getNames(method.getParameterTypes())),
|
||||
method.getReturnType().getName());
|
||||
// SPRING PATCH END
|
||||
}
|
||||
|
||||
public static Set createSet(Collection methods) {
|
||||
Set set = new HashSet();
|
||||
for (Iterator it = methods.iterator(); it.hasNext();) {
|
||||
set.add(create((Method)it.next()));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
}
|
|
@ -20,12 +20,16 @@ import java.beans.BeanInfo;
|
|||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -444,6 +448,15 @@ public class ReflectUtils {
|
|||
Class c = null;
|
||||
Throwable t = THROWABLE;
|
||||
|
||||
String generatedClasses = System.getProperty("cglib.generatedClasses");
|
||||
if (generatedClasses != null) {
|
||||
Path path = Path.of(generatedClasses + "/" + className.replace(".", "/") + ".class");
|
||||
Files.createDirectories(path.getParent());
|
||||
try (OutputStream os = Files.newOutputStream(path)) {
|
||||
new ByteArrayInputStream(b).transferTo(os);
|
||||
}
|
||||
}
|
||||
|
||||
// Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches
|
||||
if (contextClass != null && contextClass.getClassLoader() == loader) {
|
||||
try {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
|
@ -17,23 +17,36 @@
|
|||
package org.springframework.cglib.core;
|
||||
|
||||
/**
|
||||
* Custom extension of CGLIB's {@link DefaultNamingPolicy}, modifying
|
||||
* the tag in generated class names from "ByCGLIB" to "BySpringCGLIB".
|
||||
* Custom variant of CGLIB's {@link DefaultNamingPolicy}, modifying the tag
|
||||
* in generated class names from "EnhancerByCGLIB" etc to a "SpringCGLIB" tag
|
||||
* and using a plain counter suffix instead of a hash code suffix (as of 6.0).
|
||||
*
|
||||
* <p>This is primarily designed to avoid clashes between a regular CGLIB
|
||||
* version (used by some other library) and Spring's embedded variant,
|
||||
* in case the same class happens to get proxied for different purposes.
|
||||
* <p>This allows for reliably discovering pre-generated Spring proxy classes
|
||||
* in the classpath (as written at runtime when the "cglib.generatedClasses"
|
||||
* system property points to a specific directory to store the proxy classes).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.2.8
|
||||
* @since 3.2.8 / 6.0
|
||||
*/
|
||||
public class SpringNamingPolicy extends DefaultNamingPolicy {
|
||||
public final class SpringNamingPolicy implements NamingPolicy {
|
||||
|
||||
public static final SpringNamingPolicy INSTANCE = new SpringNamingPolicy();
|
||||
|
||||
@Override
|
||||
protected String getTag() {
|
||||
return "BySpringCGLIB";
|
||||
private SpringNamingPolicy() {
|
||||
}
|
||||
|
||||
public String getClassName(String prefix, String source, Object key, Predicate names) {
|
||||
if (prefix == null) {
|
||||
prefix = "org.springframework.cglib.empty.Object";
|
||||
} else if (prefix.startsWith("java")) {
|
||||
prefix = "_" + prefix;
|
||||
}
|
||||
String base = prefix + "$$SpringCGLIB$$";
|
||||
int index = 0;
|
||||
String attempt = base + index;
|
||||
while (names.evaluate(attempt))
|
||||
attempt = base + index++;
|
||||
return attempt;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -100,9 +100,6 @@ public class Enhancer extends AbstractClassGenerator {
|
|||
|
||||
private static final Source SOURCE = new Source(Enhancer.class.getName());
|
||||
|
||||
private static final EnhancerKey KEY_FACTORY =
|
||||
(EnhancerKey) KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null);
|
||||
|
||||
private static final String BOUND_FIELD = "CGLIB$BOUND";
|
||||
|
||||
private static final String FACTORY_DATA_FIELD = "CGLIB$FACTORY_DATA";
|
||||
|
@ -197,19 +194,16 @@ public class Enhancer extends AbstractClassGenerator {
|
|||
private Object currentKey;
|
||||
|
||||
|
||||
/**
|
||||
* Internal interface, only public due to ClassLoader issues.
|
||||
*/
|
||||
public interface EnhancerKey {
|
||||
|
||||
public Object newInstance(String type,
|
||||
String[] interfaces,
|
||||
// SPRING PATCH BEGIN
|
||||
private record EnhancerKey(String type,
|
||||
List<String> interfaces,
|
||||
WeakCacheKey<CallbackFilter> filter,
|
||||
Type[] callbackTypes,
|
||||
List<Type> callbackTypes,
|
||||
boolean useFactory,
|
||||
boolean interceptDuringConstruction,
|
||||
Long serialVersionUID);
|
||||
Long serialVersionUID) {
|
||||
}
|
||||
// SPRING PATCH END
|
||||
|
||||
|
||||
private Class[] interfaces;
|
||||
|
@ -561,13 +555,15 @@ public class Enhancer extends AbstractClassGenerator {
|
|||
|
||||
private Object createHelper() {
|
||||
preValidate();
|
||||
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
|
||||
ReflectUtils.getNames(interfaces),
|
||||
// SPRING PATCH BEGIN
|
||||
Object key = new EnhancerKey((superclass != null) ? superclass.getName() : null,
|
||||
(interfaces != null ? Arrays.asList(ReflectUtils.getNames(interfaces)) :null),
|
||||
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
|
||||
callbackTypes,
|
||||
Arrays.asList(callbackTypes),
|
||||
useFactory,
|
||||
interceptDuringConstruction,
|
||||
serialVersionUID);
|
||||
// SPRING PATCH END
|
||||
this.currentKey = key;
|
||||
Object result = super.create(key);
|
||||
return result;
|
||||
|
|
|
@ -660,6 +660,7 @@ public class ResolvableMethod {
|
|||
enhancer.setSuperclass(type);
|
||||
enhancer.setInterfaces(new Class<?>[] {Supplier.class});
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
|
||||
Class<?> proxyClass = enhancer.createClass();
|
||||
|
|
|
@ -659,6 +659,7 @@ public class ResolvableMethod {
|
|||
enhancer.setSuperclass(type);
|
||||
enhancer.setInterfaces(new Class<?>[] {Supplier.class});
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
|
||||
Class<?> proxyClass = enhancer.createClass();
|
||||
|
|
|
@ -786,6 +786,7 @@ public class MvcUriComponentsBuilder {
|
|||
enhancer.setSuperclass(controllerType);
|
||||
enhancer.setInterfaces(new Class<?>[] {MethodInvocationInfo.class});
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setAttemptLoad(true);
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
|
||||
Class<?> proxyClass = enhancer.createClass();
|
||||
|
|
Loading…
Reference in New Issue