【问题标题】:CGLIB with spring throws IllegalAccessError带弹簧的 CGLIB 抛出 IllegalAccessError
【发布时间】:2012-07-20 06:37:36
【问题描述】:

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

<?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>

我正在使用 cglib-nodep-2.2.2.jar 和 spring 3.1.1 这在 tomcat 或 jetty 中运行良好。但是,当我在 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

【问题讨论】:

    标签: proxy weblogic spring-aop oc4j cglib


    【解决方案1】:

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

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

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

    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) 的一个实例。

    我们使用的解决方法是覆盖 WebApplicationContext (BeanFactory) 的类加载器,以便使用“真正的”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>
    

    【讨论】:

    • 谢谢埃里克,这对我有用。我们还决定从 OC4J 迁移到 Weblogic,因为对 OC4J 的支持已经停止。希望我在 weblogic 上不需要这种解决方法 :)
    • 我们终于在 weblogic 上部署了我们的应用程序并遇到了同样的问题(之前在 OC4J 上看到过)。我使用了这个解决方案,它奏效了!再次感谢:)
    猜你喜欢
    • 2021-05-08
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    • 2018-02-14
    • 1970-01-01
    相关资源
    最近更新 更多