3

我有一个启用了 aop 的 spring 应用程序,使用 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>

我正在使用 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
4

1 回答 1

18

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

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

问题基本上是 Spring 指示 CGLIBcom.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()(in 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>
于 2012-09-24T14:16:13.760 回答