Description
Alina Mylka opened SPR-11618 and commented
The CglibAopProxy.getProxy(ClassLoader classLoader)
method should ignore the ClassLoader
passed as the parameter and instead use the classloader of the parent class.
Instead of this block of code that starts at line 178 in CglibAopProxy.java
:
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
I think we should have:
Enhancer enhancer = createEnhancer();
enhancer.setClassLoader(proxySuperClass.getClassLoader());
I have an .ear app. I deploy it on Websphere using the Module classloader policy. This means that classes from JARs that live directly in the lib folder of the .ear are loaded by the "application classloader". Additionally, each .war has its own classloader. Classes that live in the .war files are loaded by the .war classloaders. When requests are processed, the .war classloader is set as the context classloader of the thread that processes the request.
I'm attaching a maven project that generates such an .ear. When you build it and deploy it on Websphere with the Module Classloader Policy, and then go to http://localhost:9080/multiclassloader/index.xhtml - you'll get a null pointer exception coming from the PhaseListener. The session-scoped bean, gets processed, the CGLIB proxy of the bean class is generated, but the proxy doesn't work. In the debugger, it looks especially weird:
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SynchronizerTokenState implements Serializable {
private Map<String, String> visitedTokenMap;
public SynchronizerTokenState(){visitedTokenMap = new HashMap<>();}
Map<String, String> getVisitedTokenMap() {
return visitedTokenMap;
}
}
... but the visitedTokenMap
is NULL.
I debugged it a little and it seems that the problem is very similar to the one described on stack overflow. The hack suggested over there as the workaround doesn't seem to work for me though. The description of the behavior fits: the proxy is generated using the context classloader of the thread that processes the request, which is the .war classloader.
AFAIU cglib proxies will simply not work if they are generated in a different classloader than the one used by the proxied class.
When I made the suggested fix, and installed a modified version of Spring to my local maven repo: both the trivial app I'm attaching and the real app I'm working on started functioning properly, with both classloader policies. The unit tests of the Spring build seem to pass.
Affects: 4.0.2, 4.0.3
Attachments:
- 2014-03-28 10_18_07-Proxy-NPE.png (21.77 kB)
- multiclassloader.zip (16.35 kB)
Issue Links:
- "CglibAopProxy: Unable to proxy method" WARN when bean class contains static final method [SPR-11107] #15733 "CglibAopProxy: Unable to proxy method" WARN when bean class contains static final method
- Illegal reflective access operation warning for toString() on CGLIB proxies [SPR-17500] #22032 Illegal reflective access operation warning for toString() on CGLIB proxies
- Revisit CGLIB AOP proxy warnings for final methods [SPR-15436] #19997 Revisit CGLIB AOP proxy warnings for final methods
- Clarification: Spring AOP pointcuts match protected methods when CGLIB is used [SPR-15354] #19917 Clarification: Spring AOP pointcuts match protected methods when CGLIB is used