Skip to content

Commit 20a286b

Browse files
committed
ASM ClassWriter uses application ClassLoader for its getCommonSuperClass check
Issue: SPR-13695
1 parent 3aefc96 commit 20a286b

File tree

7 files changed

+187
-24
lines changed

7 files changed

+187
-24
lines changed

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

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.aop.RawTargetAccess;
3737
import org.springframework.aop.TargetSource;
3838
import org.springframework.aop.support.AopUtils;
39+
import org.springframework.cglib.core.ClassGenerator;
3940
import org.springframework.cglib.core.CodeGenerationException;
4041
import org.springframework.cglib.core.SpringNamingPolicy;
4142
import org.springframework.cglib.proxy.Callback;
@@ -186,7 +187,7 @@ public Object getProxy(ClassLoader classLoader) {
186187
enhancer.setSuperclass(proxySuperClass);
187188
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
188189
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
189-
enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
190+
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
190191

191192
Callback[] callbacks = getCallbacks(rootClass);
192193
Class<?>[] types = new Class<?>[callbacks.length];
@@ -703,6 +704,7 @@ private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
703704

704705
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
705706
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
707+
706708
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
707709
this.methodProxy = methodProxy;
708710
this.publicMethod = Modifier.isPublic(method.getModifiers());
@@ -822,8 +824,7 @@ public int accept(Method method) {
822824
if (logger.isDebugEnabled()) {
823825
logger.debug("Method has advice and optimisations are enabled: " + method);
824826
}
825-
// We know that we are optimising so we can use the
826-
// FixedStaticChainInterceptors.
827+
// We know that we are optimising so we can use the FixedStaticChainInterceptors.
827828
int index = this.fixedInterceptorMap.get(key);
828829
return (index + this.fixedInterceptorOffset);
829830
}
@@ -950,4 +951,51 @@ public int hashCode() {
950951
}
951952
}
952953

954+
955+
/**
956+
* CGLIB GeneratorStrategy variant which exposes the application ClassLoader
957+
* as thread context ClassLoader for the time of class generation
958+
* (in order for ASM to pick it up when doing common superclass resolution).
959+
*/
960+
private static class ClassLoaderAwareUndeclaredThrowableStrategy extends UndeclaredThrowableStrategy {
961+
962+
private final ClassLoader classLoader;
963+
964+
public ClassLoaderAwareUndeclaredThrowableStrategy(ClassLoader classLoader) {
965+
super(UndeclaredThrowableException.class);
966+
this.classLoader = classLoader;
967+
}
968+
969+
@Override
970+
public byte[] generate(ClassGenerator cg) throws Exception {
971+
if (this.classLoader == null) {
972+
return super.generate(cg);
973+
}
974+
975+
Thread currentThread = Thread.currentThread();
976+
ClassLoader threadContextClassLoader;
977+
try {
978+
threadContextClassLoader = currentThread.getContextClassLoader();
979+
}
980+
catch (Throwable ex) {
981+
// Cannot access thread context ClassLoader - falling back...
982+
return super.generate(cg);
983+
}
984+
985+
boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
986+
if (overrideClassLoader) {
987+
currentThread.setContextClassLoader(this.classLoader);
988+
}
989+
try {
990+
return super.generate(cg);
991+
}
992+
finally {
993+
if (overrideClassLoader) {
994+
// Reset original thread context ClassLoader.
995+
currentThread.setContextClassLoader(threadContextClassLoader);
996+
}
997+
}
998+
}
999+
}
1000+
9531001
}

spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,9 @@
2525
import org.springframework.beans.BeanInstantiationException;
2626
import org.springframework.beans.BeanUtils;
2727
import org.springframework.beans.factory.BeanFactory;
28+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
29+
import org.springframework.cglib.core.ClassGenerator;
30+
import org.springframework.cglib.core.DefaultGeneratorStrategy;
2831
import org.springframework.cglib.core.SpringNamingPolicy;
2932
import org.springframework.cglib.proxy.Callback;
3033
import org.springframework.cglib.proxy.CallbackFilter;
@@ -141,6 +144,10 @@ private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
141144
Enhancer enhancer = new Enhancer();
142145
enhancer.setSuperclass(beanDefinition.getBeanClass());
143146
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
147+
if (this.owner instanceof ConfigurableBeanFactory) {
148+
ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
149+
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
150+
}
144151
enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
145152
enhancer.setCallbackTypes(CALLBACK_TYPES);
146153
return enhancer.createClass();
@@ -178,6 +185,52 @@ public int hashCode() {
178185
}
179186

180187

188+
/**
189+
* CGLIB GeneratorStrategy variant which exposes the application ClassLoader
190+
* as thread context ClassLoader for the time of class generation
191+
* (in order for ASM to pick it up when doing common superclass resolution).
192+
*/
193+
private static class ClassLoaderAwareGeneratorStrategy extends DefaultGeneratorStrategy {
194+
195+
private final ClassLoader classLoader;
196+
197+
public ClassLoaderAwareGeneratorStrategy(ClassLoader classLoader) {
198+
this.classLoader = classLoader;
199+
}
200+
201+
@Override
202+
public byte[] generate(ClassGenerator cg) throws Exception {
203+
if (this.classLoader == null) {
204+
return super.generate(cg);
205+
}
206+
207+
Thread currentThread = Thread.currentThread();
208+
ClassLoader threadContextClassLoader;
209+
try {
210+
threadContextClassLoader = currentThread.getContextClassLoader();
211+
}
212+
catch (Throwable ex) {
213+
// Cannot access thread context ClassLoader - falling back...
214+
return super.generate(cg);
215+
}
216+
217+
boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
218+
if (overrideClassLoader) {
219+
currentThread.setContextClassLoader(this.classLoader);
220+
}
221+
try {
222+
return super.generate(cg);
223+
}
224+
finally {
225+
if (overrideClassLoader) {
226+
// Reset original thread context ClassLoader.
227+
currentThread.setContextClassLoader(threadContextClassLoader);
228+
}
229+
}
230+
}
231+
}
232+
233+
181234
/**
182235
* CGLIB callback for filtering method interception behavior.
183236
*/

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@ class ConfigurationClassEnhancer {
7979

8080
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
8181

82-
private static final DefaultGeneratorStrategy GENERATOR_STRATEGY = new BeanFactoryAwareGeneratorStrategy();
83-
8482
private static final String BEAN_FACTORY_FIELD = "$$beanFactory";
8583

8684

@@ -94,7 +92,7 @@ class ConfigurationClassEnhancer {
9492
* container-aware callbacks capable of respecting scoping and other bean semantics.
9593
* @return the enhanced subclass
9694
*/
97-
public Class<?> enhance(Class<?> configClass) {
95+
public Class<?> enhance(Class<?> configClass, ClassLoader classLoader) {
9896
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
9997
if (logger.isDebugEnabled()) {
10098
logger.debug(String.format("Ignoring request to enhance %s as it has " +
@@ -106,7 +104,7 @@ public Class<?> enhance(Class<?> configClass) {
106104
}
107105
return configClass;
108106
}
109-
Class<?> enhancedClass = createClass(newEnhancer(configClass));
107+
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
110108
if (logger.isDebugEnabled()) {
111109
logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
112110
configClass.getName(), enhancedClass.getName()));
@@ -117,13 +115,13 @@ public Class<?> enhance(Class<?> configClass) {
117115
/**
118116
* Creates a new CGLIB {@link Enhancer} instance.
119117
*/
120-
private Enhancer newEnhancer(Class<?> superclass) {
118+
private Enhancer newEnhancer(Class<?> superclass, ClassLoader classLoader) {
121119
Enhancer enhancer = new Enhancer();
122120
enhancer.setSuperclass(superclass);
123121
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
124122
enhancer.setUseFactory(false);
125123
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
126-
enhancer.setStrategy(GENERATOR_STRATEGY);
124+
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
127125
enhancer.setCallbackFilter(CALLBACK_FILTER);
128126
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
129127
return enhancer;
@@ -144,7 +142,7 @@ private Class<?> createClass(Enhancer enhancer) {
144142

145143
/**
146144
* Marker interface to be implemented by all @Configuration CGLIB subclasses.
147-
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance(Class)}
145+
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
148146
* through checking to see if candidate classes are already assignable to it, e.g.
149147
* have already been enhanced.
150148
* <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
@@ -204,9 +202,17 @@ public Class<?>[] getCallbackTypes() {
204202

205203
/**
206204
* Custom extension of CGLIB's DefaultGeneratorStrategy, introducing a {@link BeanFactory} field.
205+
* Also exposes the application ClassLoader as thread context ClassLoader for the time of
206+
* class generation (in order for ASM to pick it up when doing common superclass resolution).
207207
*/
208208
private static class BeanFactoryAwareGeneratorStrategy extends DefaultGeneratorStrategy {
209209

210+
private final ClassLoader classLoader;
211+
212+
public BeanFactoryAwareGeneratorStrategy(ClassLoader classLoader) {
213+
this.classLoader = classLoader;
214+
}
215+
210216
@Override
211217
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
212218
ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
@@ -218,6 +224,37 @@ public void end_class() {
218224
};
219225
return new TransformingClassGenerator(cg, transformer);
220226
}
227+
228+
@Override
229+
public byte[] generate(ClassGenerator cg) throws Exception {
230+
if (this.classLoader == null) {
231+
return super.generate(cg);
232+
}
233+
234+
Thread currentThread = Thread.currentThread();
235+
ClassLoader threadContextClassLoader;
236+
try {
237+
threadContextClassLoader = currentThread.getContextClassLoader();
238+
}
239+
catch (Throwable ex) {
240+
// Cannot access thread context ClassLoader - falling back...
241+
return super.generate(cg);
242+
}
243+
244+
boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
245+
if (overrideClassLoader) {
246+
currentThread.setContextClassLoader(this.classLoader);
247+
}
248+
try {
249+
return super.generate(cg);
250+
}
251+
finally {
252+
if (overrideClassLoader) {
253+
// Reset original thread context ClassLoader.
254+
currentThread.setContextClassLoader(threadContextClassLoader);
255+
}
256+
}
257+
}
221258
}
222259

223260

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,11 @@ public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFact
397397
try {
398398
// Set enhanced subclass of the user-specified bean class
399399
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
400-
Class<?> enhancedClass = enhancer.enhance(configClass);
400+
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
401401
if (configClass != enhancedClass) {
402402
if (logger.isDebugEnabled()) {
403-
logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' " +
404-
"with enhanced class name '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
403+
logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " +
404+
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
405405
}
406406
beanDef.setBeanClass(enhancedClass);
407407
}

spring-context/src/test/java/org/springframework/context/annotation/ReflectionUtilsIntegrationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ public class ReflectionUtilsIntegrationTests {
3737

3838
@Test
3939
public void getUniqueDeclaredMethods_withCovariantReturnType_andCglibRewrittenMethodNames() throws Exception {
40-
Class<?> cglibLeaf = new ConfigurationClassEnhancer().enhance(Leaf.class);
40+
Class<?> cglibLeaf = new ConfigurationClassEnhancer().enhance(Leaf.class, null);
4141
int m1MethodCount = 0;
4242
Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(cglibLeaf);
4343
for (Method method : methods) {

spring-core/src/main/java/org/springframework/asm/ClassWriter.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1676,7 +1676,8 @@ int getMergedType(final int type1, final int type2) {
16761676
*/
16771677
protected String getCommonSuperClass(final String type1, final String type2) {
16781678
Class<?> c, d;
1679-
ClassLoader classLoader = getClass().getClassLoader();
1679+
// SPRING PATCH: PREFER APPLICATION CLASSLOADER
1680+
ClassLoader classLoader = getClassLoader();
16801681
try {
16811682
c = Class.forName(type1.replace('/', '.'), false, classLoader);
16821683
d = Class.forName(type2.replace('/', '.'), false, classLoader);
@@ -1699,6 +1700,17 @@ protected String getCommonSuperClass(final String type1, final String type2) {
16991700
}
17001701
}
17011702

1703+
// SPRING PATCH: PREFER THREAD CONTEXT CLASSLOADER FOR APPLICATION CLASSES
1704+
protected ClassLoader getClassLoader() {
1705+
ClassLoader classLoader = null;
1706+
try {
1707+
classLoader = Thread.currentThread().getContextClassLoader();
1708+
} catch (Throwable ex) {
1709+
// Cannot access thread context ClassLoader - falling back...
1710+
}
1711+
return (classLoader != null ? classLoader : getClass().getClassLoader());
1712+
}
1713+
17021714
/**
17031715
* Returns the constant pool's hash table item which is equal to the given
17041716
* item.

spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelCompiler.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -134,7 +134,7 @@ private int getNextSuffix() {
134134
private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) {
135135
// Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression'
136136
String clazzName = "spel/Ex" + getNextSuffix();
137-
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
137+
ClassWriter cw = new ExpressionClassWriter();
138138
cw.visit(V1_5, ACC_PUBLIC, clazzName, null, "org/springframework/expression/spel/CompiledExpression", null);
139139

140140
// Create default constructor
@@ -256,25 +256,38 @@ private static void dump(String expressionText, String name, byte[] bytecode) {
256256
}
257257
catch (IOException ex) {
258258
throw new IllegalStateException(
259-
"Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex);
259+
"Unexpected problem dumping class '" + nameToUse + "' into " + dumpLocation, ex);
260260
}
261261
}
262262

263263

264264
/**
265-
* A ChildClassLoader will load the generated compiled expression classes
265+
* A ChildClassLoader will load the generated compiled expression classes.
266266
*/
267-
public static class ChildClassLoader extends URLClassLoader {
267+
private static class ChildClassLoader extends URLClassLoader {
268268

269269
private static final URL[] NO_URLS = new URL[0];
270270

271-
public ChildClassLoader(ClassLoader classloader) {
272-
super(NO_URLS, classloader);
271+
public ChildClassLoader(ClassLoader classLoader) {
272+
super(NO_URLS, classLoader);
273273
}
274274

275275
public Class<?> defineClass(String name, byte[] bytes) {
276276
return super.defineClass(name, bytes, 0, bytes.length);
277277
}
278278
}
279279

280+
281+
private class ExpressionClassWriter extends ClassWriter {
282+
283+
public ExpressionClassWriter() {
284+
super(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
285+
}
286+
287+
@Override
288+
protected ClassLoader getClassLoader() {
289+
return ccl;
290+
}
291+
}
292+
280293
}

0 commit comments

Comments
 (0)