2008-12-04 8 views
9

Tengo una aplicación web de Spring que está configurada para usar proxys JDK para AOP. Las anotaciones de AOP (como @Transactional) se declaran en las interfaces, en lugar de las clases de implementación.Problema de AOP en ejecución Pruebas de unidad de resorte

La aplicación en sí funciona bien, pero cuando ejecuto las pruebas de la unidad, parece que estoy intentando usar CGLIB para la funcionalidad AOP (en lugar del proxying JDK). Esto hace que las pruebas fallen; he adjuntado el seguimiento de la pila a continuación.

No entiendo por qué CGLIB se está utilizando cuando ejecuto las pruebas, porque la configuración de Spring es en gran medida la misma que cuando se ejecuta la aplicación. Una diferencia posiblemente significativa es que la configuración de prueba utiliza un DataSourceTransactionManager en lugar de un administrador de transacciones JTA. Todas las clases de prueba extienden AbstractJUnit4SpringContextTests, ¿podría ser que esta clase de alguna manera esté cableada para usar CGLIB?

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: Uno de los comentaristas solicitaron que he puesto la configuración de Spring. Lo he incluido a continuación en forma abreviada (es decir, los beans irrelevantes y los espacios de nombres XML omitidos).

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

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

applicationContext-test.xml Esto sólo se incluye cuando se ejecuta las pruebas de unidad. Su propósito es sobrescribir algunos de los beans declarados en los otros archivos de configuración.

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

Pensé que esto podría obtener más miradas con las etiquetas "java" y "junit", así que repasé. Ojalá pudiera ayudar con la respuesta! –

+0

Hmm, interesante, ejecuto una configuración similar muy similar a la tuya sin problemas. ¿Puedes publicar algún código de muestra (incluidos tus archivos xml de contexto)? Además, ¿tienes otras dependencias en cglib? Aun así, ¿puedes tratar de eliminarlo de la ruta de clase solo para ver si los beans se conectan? –

Respuesta

3

Oye Los proxies de Jean CGLib se crean subclasificando la clase que se va a usar como proxy: estás intentando proxy otro proxy que no está permitido ya que los proxies son en sí mismos clases finales. Por lo tanto:

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

+0

Gracias, pero ¿hay alguna idea de por qué está pasando esto? –

+0

Sin tener una aplicación de Spring delante de mí para probar, sospecho que es porque está incluyendo los contextos de prueba y prueba cuando arranca el contenedor. Intentaría externalizar el consejo en su propio archivo y luego crear un archivo para cada una de las fuentes de datos de prueba y prueba y tx-managers. –

+0

Tuve un problema similar con varias instancias de la misma ID de bean. –

2

No sé si la solución ya era compartido y también estoy seguro de que el solicitante original debe haber encontrado una solución, ya que es una consulta de un año de edad. Sin embargo, para el interés público, permítanme mencionarlo aquí. Spring estaba usando CGLIB debido a la siguiente declaración.

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

La propiedad debe establecerse en false, por lo que el CGLIB no se desencadena en lugar de JDK dinámica del uso de proxy.

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

Espero que ayude.

Cuestiones relacionadas