ASM ClassWriter uses application ClassLoader for its getCommonSuperClass check

Issue: SPR-13695
This commit is contained in:
Juergen Hoeller 2015-11-24 17:38:02 +01:00
parent 3aefc96593
commit 20a286b4d6
7 changed files with 187 additions and 24 deletions

View File

@ -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);
}
}
}
}
}

View File

@ -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.
*/

View File

@ -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);
}
}
}
}

View File

@ -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);
}

View File

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

View File

@ -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.

View File

@ -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;
}
}
}