带弹簧的CGLIB抛出IllegalAccessError

时间:2012-07-20 06:37:36

标签: proxy weblogic spring-aop oc4j cglib

我有一个Spring应用程序,启用了aop,使用cglib代理进行日志记录:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy proxy-target-class="true"/>

<bean id="loggingPointcuts" class="com.coverall.integration.commons.logging.LoggingPointcuts"/>

<bean id="loggingAspect" class="com.coverall.integration.commons.logging.LoggingAspect"/>
</beans>

我正在使用带有spring 3.1.1的cglib-nodep-2.2.2.jar 这在tomcat或码头中效果很好。但是,当我在OC4J(使用jdk1.6)上部署它时,我收到以下错误: 它试图代理的类 - ComponentRegistryImpl是包私有

Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.company.int.components.core.registration.ComponentRegistryImpl]: Common causes of this problem include using a final class or a non-visible class; nested exception is net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
    at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:207) ~[org.springframework.aop-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:112) ~[org.springframework.aop-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:476) ~[org.springframework.aop-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:362) ~[org.springframework.aop-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322) ~[org.springframework.aop-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:407) [org.springframework.beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1461) [org.springframework.beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) [org.springframework.beans-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    ... 40 common frames omitted
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237) ~[cglib-nodep-2.2.2.jar:na]
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) ~[cglib-nodep-2.2.2.jar:na]
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) ~[cglib-nodep-2.2.2.jar:na]
    at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:201) ~[org.springframework.aop-3.1.1.RELEASE.jar:3.1.1.RELEASE]
    ... 47 common frames omitted
Caused by: java.lang.reflect.InvocationTargetException: null
    at sun.reflect.GeneratedMethodAccessor12.invoke(Unknown Source) ~[na:na]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_20]
    at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_20]
    at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384) ~[cglib-nodep-2.2.2.jar:na]
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219) ~[cglib-nodep-2.2.2.jar:na]
    ... 50 common frames omitted
Caused by: java.lang.IllegalAccessError: class com.company.int.components.core.registration.ComponentRegistryImpl$$EnhancerByCGLIB$$730712da cannot access its superclass com.company.int.components.core.registration.ComponentRegistryImpl
    at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.6.0_20]
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) ~[na:1.6.0_20]
    at java.lang.ClassLoader.defineClass(ClassLoader.java:616) ~[na:1.6.0_20]
    ... 55 common frames omitted

1 个答案:

答案 0 :(得分:17)

我不确定OC4J,但是我们在JBoss 6上遇到了完全相同的错误。

通常,CGLIB可以增强(代理)包私有的类。这在Jetty&amp; amp;正如你所观察到的Tomcat。但是,它在默认的JBoss 6类加载器设置中不起作用。

问题基本上是Spring正在指示CGLIB使用与目标类(超类,com.company.int.components.core.registration.ComponentRegistryImpl$$EnhancerByCGLIB$$730712da)不同的类加载器来创建代理类(子类com.company.int.components.core.registration.ComponentRegistryImpl)。因此,虽然这些类在同一个文本包中,但它们实际上是在运行时在不同的包中创建的(因为它们位于不同的类加载器中)。

CGLIB默认在与代理目标相同的类加载器中定义代理类。但是,Spring会覆盖CGLIB增强器使用的类加载器。来自org.springframework.aop.framework.Cglib2AopProxy.getProxy(ClassLoader)

            // Configure CGLIB Enhancer...
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }

跟踪该类加载器的数据流表明它来自BeanFactory本身定义的类加载器,默认为Thread.currentThread().getContextClassLoader()(在org.springframework.util.ClassUtils.getDefaultClassLoader()中)。

在理智的servlet容器(如Jetty和Tomcat)中,上下文类加载器与加载所有应用程序类的类加载器相同(在WEB-INF / lib或WEB-INF / classes中)。但是,在JBoss 6中(我猜测OC4J),这两者并不相同。上下文类加载器实际上是webapp类加载器的子代。它实际上没有定义任何类,只是委托父进程。在JBoss中,上下文类加载器是WebCtxLoader$ENCLoader)的实例。

我们使用的解决方法是覆盖WebApplicationContextBeanFactory)的类加载器,以便使用“真正的”webapp类加载器。我们通过创建自己的ContextLoaderListener来实现这一点(加载MyContextLoaderListener实现的类加载器就是我们想要的那个):

public class MyContextLoaderListener extends ContextLoaderListener {
    @Override
    protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
        ClassLoader classLoader = getClass().getClassLoader();
        logger.debug("Overriding WebApplicationContext classloader from %s to %s", applicationContext.getClassLoader(), classLoader);
        ((DefaultResourceLoader) applicationContext).setClassLoader(classLoader);
    }
}

您需要在web.xml中添加类作为上下文侦听器:

<listener>
    <listener-class>com.company.MyContextLoaderListener</listener-class>
</listener>