2008-12-04 8 views
9

Ich habe eine Spring-Webanwendung, die für die Verwendung von JDK-Proxys für AOP konfiguriert ist. Die AOP-Annotationen (z. B. @Transactional) werden auf den Schnittstellen und nicht in den Implementierungsklassen deklariert.AOP-Problem beim Ausführen von Spring Unit-Tests

Die Anwendung selbst funktioniert gut, aber wenn ich die Komponententests ausführen, scheint es versucht zu sein, CGLIB für die AOP-Funktionalität zu verwenden (anstelle von JDK-Proxy). Dies führt dazu, dass die Tests fehlschlagen. Ich habe die Stapelverfolgung unten angehängt.

Ich verstehe nicht, warum CGLIB verwendet wird, wenn ich die Tests ausführen, da die Spring-Konfiguration weitgehend identisch ist, als wenn die Anwendung ausgeführt wird. Ein möglicherweise signifikanter Unterschied besteht darin, dass die Testkonfiguration einen DataSourceTransactionManager anstelle eines JTA-Transaktionsmanagers verwendet. Die Testklassen selbst alle erweitern AbstractJUnit4SpringContextTests, könnte es sein, dass diese Klasse irgendwie fest verdrahtet ist, um CGLIB zu verwenden?

Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class $Proxy25]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25 
    at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:213) 
    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110) 
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:488) 
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:363) 
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:324) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:361) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1343) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473) 
    ... 79 more 
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25 
    at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446) 
    at net.sf.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33) 
    at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) 
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) 
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) 
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) 
    at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:201) 
    ... 86 more 

EDIT: Einer der Kommentatoren gefordert, dass ich die Spring-Konfiguration posten. Ich habe es unten in abgekürzter Form eingefügt (d. H. Irrelevante Beans und XML-Namespaces wurden weggelassen).

feder servlet.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans>     
    <!-- ANNOTATION SUPPORT --> 
    <!-- Include basic annotation support --> 
    <context:annotation-config/>   

    <!-- CONTROLLERS --> 
    <!-- Controllers, force scanning --> 
    <context:component-scan base-package="com.onebigplanet.web.controller,com.onebigplanet.web.ws.*"/> 

    <!-- Post-processor for @Aspect annotated beans, which converts them into AOP advice --> 
    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"> 
     <property name="proxyTargetClass" value="true"/> 
    </bean> 

    <!-- An @Aspect bean that converts exceptions thrown in POJO service implementation classes to runtime exceptions --> 
    <bean id="permissionAdvisor" class="com.onebigplanet.web.advisor.PermissionAdvisor"/> 
    <bean id="businessIntelligenceAdvisor" class="com.onebigplanet.web.advisor.bi.BusinessIntelligenceAdvisor"/>   

    <!-- Finds the controllers and sets an interceptor on each one --> 
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
     <property name="interceptors"> 
      <list> 
       <bean class="com.onebigplanet.web.interceptor.PortalInterceptor"/>    
      </list> 
     </property> 
    </bean> 

    <!-- METHOD HANDLER ADAPTER --> 
    <!-- Finds mapping of url through annotation on methods of Controller --> 
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
     <property name="cacheSeconds" value="0"/> 
     <property name="webBindingInitializer"> 
      <bean class="com.onebigplanet.web.binder.WebBindingInitializer"/> 
     </property> 
    </bean> 
</beans> 

application-service.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans> 
    <!-- Declares a bunch of bean post-processors --> 
    <context:annotation-config/> 

    <context:component-scan base-package="com.onebigplanet.service.impl,com.onebigplanet.dao.impl.mysql" annotation-config="false"/>  

    <!-- Property configurer --> 
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
     <property name="location" value="/WEB-INF/obp-service.properties" /> 
    </bean> 

    <!-- Post-processor for @Aspect annotated beans, which converts them into AOP advice --> 
    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/> 

    <!-- An @Aspect bean that converts exceptions thrown in service implementation classes to runtime exceptions --> 
    <bean id="exceptionAdvisor" class="com.onebigplanet.service.advisor.ExceptionAdvisor"/> 
    <bean id="cachingAdvisor" class="com.onebigplanet.service.advisor.CacheAdvisor"/> 
    <bean id="businessIntelligenceAffiliateAdvisor" class="com.onebigplanet.service.advisor.BusinessIntelligenceAffiliateAdvisor"/> 

    <!-- Writable datasource --> 
    <jee:jndi-lookup id="dataSource" jndi-name="java:/ObpDS"/> 

    <!-- ReadOnly datasource --> 
    <jee:jndi-lookup id="readOnlyDataSource" jndi-name="java:/ObpReadOnlyDS"/> 

    <!-- Map the transaction manager to allow easy lookup of a UserTransaction --> 
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> 

    <!-- Annotation driven transaction management --> 
    <tx:annotation-driven transaction-manager="transactionManager"/> 
</beans> 

application-test.xml Dies wird nur aufgenommen, wenn die Einheit-Tests ausgeführt wird. Der Zweck besteht darin, einige der in den anderen Konfigurationsdateien deklarierten Beans zu überschreiben.

<?xml version="1.0" encoding="UTF-8"?> 
<beans>   
    <!-- Overwrite the property configurer bean such that it reads the test properties file instead --> 
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
     <property name="location" value="/obp-test.properties"/> 
    </bean> 

    <!-- All DAOs should use the test datasource --> 
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 
     <property name="driverClassName" value="${testDataSource.driverClassName}"/> 
     <property name="url" value="${testDataSource.url}"/> 
     <property name="username" value="${testDataSource.username}"/> 
     <property name="password" value="${testDataSource.password}"/> 
    </bean> 

    <bean id="readOnlyDataSource" class="org.apache.commons.dbcp.BasicDataSource"> 
     <property name="driverClassName" value="${testDataSource.driverClassName}"/> 
     <property name="url" value="${testDataSource.url}"/> 
     <property name="username" value="${testDataSource.username}"/> 
     <property name="password" value="${testDataSource.password}"/> 
    </bean> 

    <!-- 
     Overwrite the JTA transaction manager bean defined in applicationContent-service.xml with this one because 
     the implementation of the former is provided by JBoss 
    --> 
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
     <property name="dataSource" ref="dataSource" /> 
    </bean> 
<beans> 
+0

Ich dachte, das könnte mehr aussehen mit "Java" und "Junit" Tags, also habe ich retagged. Ich wünschte, ich könnte mit der Antwort helfen! –

+0

Hmm, interessant, ich laufe ein ähnliches Setup, das sehr ähnlich zu deinem ist, ohne Probleme. Können Sie Beispielcode (einschließlich Ihrer Kontext-XML-Dateien) posten? Haben Sie weitere Abhängigkeiten zu cglib? Kannst du es trotzdem versuchen, es aus dem Klassenpfad zu entfernen, nur um zu sehen, ob die Beans verdrahten? –

Antwort

3

Hey Jean, CGLib-Proxies werden erstellt, indem die zu proxiesierende Klasse abgeleitet wird - Sie versuchen, einen anderen Proxy zu proxy, der nicht erlaubt ist, da Proxies selbst letzte Klassen sind. Daraus folgt:

Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25

+0

Danke, aber irgendeine Idee, warum das passiert? –

+0

Ohne eine Spring-App vor mir zu testen, vermute ich, weil Sie sowohl den prod- als auch den test-Kontext beim Bootstrap des Containers einschließen. Ich würde versuchen, den Rat in seine eigene Datei zu externalisieren und dann eine Datei für jede der Prod- und Testdatenquellen und tx-Manager zu erstellen. –

+0

Ich hatte ein ähnliches Problem mit mehreren Instanzen der gleichen Bean-ID. –

2

Ich weiß nicht, ob die Lösung bereits freigegeben wurde, und ich bin auch sicher, dass der ursprüngliche Anforderer eine Lösung gefunden haben muss, da es sich um eine ein Jahr alt Abfrage. Aus Gründen des öffentlichen Interesses möchte ich es hier jedoch erwähnen. Spring verwendete CGLIB wegen der folgenden Deklaration.

Die Eigenschaft sollte auf "false" gesetzt sein, damit das CGLIB nicht statt JDK Dynamic Proxying ausgelöst wird.

<property name="proxyTargetClass" value="false"/> 

Hoffe, dass hilft.