ASM ClassWriter uses application ClassLoader for its getCommonSuperClass check
Issue: SPR-13695
This commit is contained in:
parent
3aefc96593
commit
20a286b4d6
|
|
@ -36,6 +36,7 @@ import org.springframework.aop.PointcutAdvisor;
|
|||
import org.springframework.aop.RawTargetAccess;
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.cglib.core.ClassGenerator;
|
||||
import org.springframework.cglib.core.CodeGenerationException;
|
||||
import org.springframework.cglib.core.SpringNamingPolicy;
|
||||
import org.springframework.cglib.proxy.Callback;
|
||||
|
|
@ -186,7 +187,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
|||
enhancer.setSuperclass(proxySuperClass);
|
||||
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
|
||||
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
|
||||
|
||||
Callback[] callbacks = getCallbacks(rootClass);
|
||||
Class<?>[] types = new Class<?>[callbacks.length];
|
||||
|
|
@ -703,6 +704,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
|||
|
||||
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
|
||||
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
|
||||
|
||||
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
|
||||
this.methodProxy = methodProxy;
|
||||
this.publicMethod = Modifier.isPublic(method.getModifiers());
|
||||
|
|
@ -822,8 +824,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Method has advice and optimisations are enabled: " + method);
|
||||
}
|
||||
// We know that we are optimising so we can use the
|
||||
// FixedStaticChainInterceptors.
|
||||
// We know that we are optimising so we can use the FixedStaticChainInterceptors.
|
||||
int index = this.fixedInterceptorMap.get(key);
|
||||
return (index + this.fixedInterceptorOffset);
|
||||
}
|
||||
|
|
@ -950,4 +951,51 @@ class CglibAopProxy implements AopProxy, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CGLIB GeneratorStrategy variant which exposes the application ClassLoader
|
||||
* as thread context ClassLoader for the time of class generation
|
||||
* (in order for ASM to pick it up when doing common superclass resolution).
|
||||
*/
|
||||
private static class ClassLoaderAwareUndeclaredThrowableStrategy extends UndeclaredThrowableStrategy {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ClassLoaderAwareUndeclaredThrowableStrategy(ClassLoader classLoader) {
|
||||
super(UndeclaredThrowableException.class);
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generate(ClassGenerator cg) throws Exception {
|
||||
if (this.classLoader == null) {
|
||||
return super.generate(cg);
|
||||
}
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
ClassLoader threadContextClassLoader;
|
||||
try {
|
||||
threadContextClassLoader = currentThread.getContextClassLoader();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Cannot access thread context ClassLoader - falling back...
|
||||
return super.generate(cg);
|
||||
}
|
||||
|
||||
boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
|
||||
if (overrideClassLoader) {
|
||||
currentThread.setContextClassLoader(this.classLoader);
|
||||
}
|
||||
try {
|
||||
return super.generate(cg);
|
||||
}
|
||||
finally {
|
||||
if (overrideClassLoader) {
|
||||
// Reset original thread context ClassLoader.
|
||||
currentThread.setContextClassLoader(threadContextClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -25,6 +25,9 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.beans.BeanInstantiationException;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.cglib.core.ClassGenerator;
|
||||
import org.springframework.cglib.core.DefaultGeneratorStrategy;
|
||||
import org.springframework.cglib.core.SpringNamingPolicy;
|
||||
import org.springframework.cglib.proxy.Callback;
|
||||
import org.springframework.cglib.proxy.CallbackFilter;
|
||||
|
|
@ -141,6 +144,10 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
|||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(beanDefinition.getBeanClass());
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
if (this.owner instanceof ConfigurableBeanFactory) {
|
||||
ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
|
||||
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
|
||||
}
|
||||
enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
|
||||
enhancer.setCallbackTypes(CALLBACK_TYPES);
|
||||
return enhancer.createClass();
|
||||
|
|
@ -178,6 +185,52 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* CGLIB GeneratorStrategy variant which exposes the application ClassLoader
|
||||
* as thread context ClassLoader for the time of class generation
|
||||
* (in order for ASM to pick it up when doing common superclass resolution).
|
||||
*/
|
||||
private static class ClassLoaderAwareGeneratorStrategy extends DefaultGeneratorStrategy {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ClassLoaderAwareGeneratorStrategy(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generate(ClassGenerator cg) throws Exception {
|
||||
if (this.classLoader == null) {
|
||||
return super.generate(cg);
|
||||
}
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
ClassLoader threadContextClassLoader;
|
||||
try {
|
||||
threadContextClassLoader = currentThread.getContextClassLoader();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Cannot access thread context ClassLoader - falling back...
|
||||
return super.generate(cg);
|
||||
}
|
||||
|
||||
boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
|
||||
if (overrideClassLoader) {
|
||||
currentThread.setContextClassLoader(this.classLoader);
|
||||
}
|
||||
try {
|
||||
return super.generate(cg);
|
||||
}
|
||||
finally {
|
||||
if (overrideClassLoader) {
|
||||
// Reset original thread context ClassLoader.
|
||||
currentThread.setContextClassLoader(threadContextClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CGLIB callback for filtering method interception behavior.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -79,8 +79,6 @@ class ConfigurationClassEnhancer {
|
|||
|
||||
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
|
||||
|
||||
private static final DefaultGeneratorStrategy GENERATOR_STRATEGY = new BeanFactoryAwareGeneratorStrategy();
|
||||
|
||||
private static final String BEAN_FACTORY_FIELD = "$$beanFactory";
|
||||
|
||||
|
||||
|
|
@ -94,7 +92,7 @@ class ConfigurationClassEnhancer {
|
|||
* container-aware callbacks capable of respecting scoping and other bean semantics.
|
||||
* @return the enhanced subclass
|
||||
*/
|
||||
public Class<?> enhance(Class<?> configClass) {
|
||||
public Class<?> enhance(Class<?> configClass, ClassLoader classLoader) {
|
||||
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Ignoring request to enhance %s as it has " +
|
||||
|
|
@ -106,7 +104,7 @@ class ConfigurationClassEnhancer {
|
|||
}
|
||||
return configClass;
|
||||
}
|
||||
Class<?> enhancedClass = createClass(newEnhancer(configClass));
|
||||
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
||||
configClass.getName(), enhancedClass.getName()));
|
||||
|
|
@ -117,13 +115,13 @@ class ConfigurationClassEnhancer {
|
|||
/**
|
||||
* Creates a new CGLIB {@link Enhancer} instance.
|
||||
*/
|
||||
private Enhancer newEnhancer(Class<?> superclass) {
|
||||
private Enhancer newEnhancer(Class<?> superclass, ClassLoader classLoader) {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(superclass);
|
||||
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
|
||||
enhancer.setUseFactory(false);
|
||||
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
|
||||
enhancer.setStrategy(GENERATOR_STRATEGY);
|
||||
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
|
||||
enhancer.setCallbackFilter(CALLBACK_FILTER);
|
||||
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
|
||||
return enhancer;
|
||||
|
|
@ -144,7 +142,7 @@ class ConfigurationClassEnhancer {
|
|||
|
||||
/**
|
||||
* Marker interface to be implemented by all @Configuration CGLIB subclasses.
|
||||
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance(Class)}
|
||||
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
|
||||
* through checking to see if candidate classes are already assignable to it, e.g.
|
||||
* have already been enhanced.
|
||||
* <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
|
||||
|
|
@ -204,9 +202,17 @@ class ConfigurationClassEnhancer {
|
|||
|
||||
/**
|
||||
* Custom extension of CGLIB's DefaultGeneratorStrategy, introducing a {@link BeanFactory} field.
|
||||
* Also exposes the application ClassLoader as thread context ClassLoader for the time of
|
||||
* class generation (in order for ASM to pick it up when doing common superclass resolution).
|
||||
*/
|
||||
private static class BeanFactoryAwareGeneratorStrategy extends DefaultGeneratorStrategy {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public BeanFactoryAwareGeneratorStrategy(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
|
||||
ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
|
||||
|
|
@ -218,6 +224,37 @@ class ConfigurationClassEnhancer {
|
|||
};
|
||||
return new TransformingClassGenerator(cg, transformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generate(ClassGenerator cg) throws Exception {
|
||||
if (this.classLoader == null) {
|
||||
return super.generate(cg);
|
||||
}
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
ClassLoader threadContextClassLoader;
|
||||
try {
|
||||
threadContextClassLoader = currentThread.getContextClassLoader();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Cannot access thread context ClassLoader - falling back...
|
||||
return super.generate(cg);
|
||||
}
|
||||
|
||||
boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
|
||||
if (overrideClassLoader) {
|
||||
currentThread.setContextClassLoader(this.classLoader);
|
||||
}
|
||||
try {
|
||||
return super.generate(cg);
|
||||
}
|
||||
finally {
|
||||
if (overrideClassLoader) {
|
||||
// Reset original thread context ClassLoader.
|
||||
currentThread.setContextClassLoader(threadContextClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -397,11 +397,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
try {
|
||||
// Set enhanced subclass of the user-specified bean class
|
||||
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
|
||||
Class<?> enhancedClass = enhancer.enhance(configClass);
|
||||
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
|
||||
if (configClass != enhancedClass) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' " +
|
||||
"with enhanced class name '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
|
||||
logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " +
|
||||
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
|
||||
}
|
||||
beanDef.setBeanClass(enhancedClass);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -37,7 +37,7 @@ public class ReflectionUtilsIntegrationTests {
|
|||
|
||||
@Test
|
||||
public void getUniqueDeclaredMethods_withCovariantReturnType_andCglibRewrittenMethodNames() throws Exception {
|
||||
Class<?> cglibLeaf = new ConfigurationClassEnhancer().enhance(Leaf.class);
|
||||
Class<?> cglibLeaf = new ConfigurationClassEnhancer().enhance(Leaf.class, null);
|
||||
int m1MethodCount = 0;
|
||||
Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(cglibLeaf);
|
||||
for (Method method : methods) {
|
||||
|
|
|
|||
|
|
@ -1676,7 +1676,8 @@ public class ClassWriter extends ClassVisitor {
|
|||
*/
|
||||
protected String getCommonSuperClass(final String type1, final String type2) {
|
||||
Class<?> c, d;
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
// SPRING PATCH: PREFER APPLICATION CLASSLOADER
|
||||
ClassLoader classLoader = getClassLoader();
|
||||
try {
|
||||
c = Class.forName(type1.replace('/', '.'), false, classLoader);
|
||||
d = Class.forName(type2.replace('/', '.'), false, classLoader);
|
||||
|
|
@ -1699,6 +1700,17 @@ public class ClassWriter extends ClassVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// SPRING PATCH: PREFER THREAD CONTEXT CLASSLOADER FOR APPLICATION CLASSES
|
||||
protected ClassLoader getClassLoader() {
|
||||
ClassLoader classLoader = null;
|
||||
try {
|
||||
classLoader = Thread.currentThread().getContextClassLoader();
|
||||
} catch (Throwable ex) {
|
||||
// Cannot access thread context ClassLoader - falling back...
|
||||
}
|
||||
return (classLoader != null ? classLoader : getClass().getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the constant pool's hash table item which is equal to the given
|
||||
* item.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -134,7 +134,7 @@ public class SpelCompiler implements Opcodes {
|
|||
private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) {
|
||||
// Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression'
|
||||
String clazzName = "spel/Ex" + getNextSuffix();
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
|
||||
ClassWriter cw = new ExpressionClassWriter();
|
||||
cw.visit(V1_5, ACC_PUBLIC, clazzName, null, "org/springframework/expression/spel/CompiledExpression", null);
|
||||
|
||||
// Create default constructor
|
||||
|
|
@ -256,20 +256,20 @@ public class SpelCompiler implements Opcodes {
|
|||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex);
|
||||
"Unexpected problem dumping class '" + nameToUse + "' into " + dumpLocation, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A ChildClassLoader will load the generated compiled expression classes
|
||||
* A ChildClassLoader will load the generated compiled expression classes.
|
||||
*/
|
||||
public static class ChildClassLoader extends URLClassLoader {
|
||||
private static class ChildClassLoader extends URLClassLoader {
|
||||
|
||||
private static final URL[] NO_URLS = new URL[0];
|
||||
|
||||
public ChildClassLoader(ClassLoader classloader) {
|
||||
super(NO_URLS, classloader);
|
||||
public ChildClassLoader(ClassLoader classLoader) {
|
||||
super(NO_URLS, classLoader);
|
||||
}
|
||||
|
||||
public Class<?> defineClass(String name, byte[] bytes) {
|
||||
|
|
@ -277,4 +277,17 @@ public class SpelCompiler implements Opcodes {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private class ExpressionClassWriter extends ClassWriter {
|
||||
|
||||
public ExpressionClassWriter() {
|
||||
super(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassLoader getClassLoader() {
|
||||
return ccl;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue