3

ロギングに 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>

Spring 3.1.1 で cglib-nodep-2.2.2.jar を使用しています。これは、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 が 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()(in org.springframework.util.ClassUtils.getDefaultClassLoader()) です。

正常なサーブレット コンテナー (Jetty や Tomcat など) では、コンテキスト クラスローダーは、すべてのアプリケーション クラス (WEB-INF/lib または WEB-INF/classes 内) をロードするクラスローダーと同じです。ただし、JBoss 6 (および私は OC4J と推測しています) では、これら 2 つは同じではありません。コンテキスト クラスローダーは、実際には 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 に答える