Java 运行 Spring 单元测试的 AOP 问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/341624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AOP problem running Spring unit tests
提问by
I have a Spring web application which is configured to use JDK proxying for AOP. The AOP annotations (such as @Transactional) are declared on the interfaces, rather than the implementation classes.
我有一个 Spring Web 应用程序,它被配置为对 AOP 使用 JDK 代理。AOP 注释(例如@Transactional)是在接口上声明的,而不是在实现类上声明的。
The application itself works fine, but when I run the unit tests, it seems to be attempting to use CGLIB for the AOP functionality (instead of JDK proxying). This causes the tests to fail - I've appended the stack trace below.
应用程序本身运行良好,但是当我运行单元测试时,它似乎试图将 CGLIB 用于 AOP 功能(而不是 JDK 代理)。这会导致测试失败 - 我在下面附加了堆栈跟踪。
I don't understand why CGLIB is being used when I run the tests, because the Spring configuration is largely the same as when the application is running. One possibly significant difference is that the test configuration uses a DataSourceTransactionManagerinstead of a JTA transaction manager. The test classes themselves all extend AbstractJUnit4SpringContextTests, could it be that this class is somehow hard-wired to use CGLIB?
我不明白为什么在我运行测试时使用 CGLIB,因为 Spring 配置与应用程序运行时的配置基本相同。一个可能的显着差异是测试配置使用DataSourceTransactionManager而不是 JTA 事务管理器。测试类本身都扩展了AbstractJUnit4SpringContextTests,这个类是否以某种方式硬连接到使用 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: One of the commentators requested that I post the Spring configuration. I've included it below in abbreviated form (i.e. irrelevant beans and XML namespaces omitted).
编辑:其中一位评论员要求我发布 Spring 配置。我以缩写形式将其包含在下面(即省略了不相关的 bean 和 XML 命名空间)。
spring-servlet.xml
spring-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
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.xmlThis is only included when running the unit tests. It's purpose is to overwrite some of the beans declared in the other config files.
applicationContext-test.xml这仅在运行单元测试时包含。它的目的是覆盖其他配置文件中声明的一些 bean。
<?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>
回答by digitalsanctum
Sounds like you're referencing an implementation class instead of an interface. There is an excerpt herewith more detail.
听起来您是在引用实现类而不是接口。这里有一个更详细的摘录。
A Spring forum post: "Mixing JDK and CGLIB proxies"
Spring 论坛帖子:“混合 JDK 和 CGLIB 代理”
A great blog post explaining pros and cons of JDK vs. CGLIB proxies.
一篇很好的博客文章,解释了 JDK 与 CGLIB 代理的优缺点。
回答by jonathan.cone
Hey Jean, CGLib proxies are created by subclassing the class to be proxied -- you're attempting to proxy another proxy which isn't allowed since proxies are themselves final classes. Hence:
嘿,Jean,CGLib 代理是通过对要代理的类进行子类化来创建的——您正在尝试代理另一个不允许的代理,因为代理本身就是最终类。因此:
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class $Proxy25
回答by Rishik Dhar
I don't know if the solution was already shared and I am also sure the original requester must have found a solution, since it is a one year old query. For public interest however let me mention it here. Spring was using CGLIB because of the following declaration.
我不知道该解决方案是否已经共享,而且我也确定原始请求者一定已经找到了解决方案,因为这是一个已有一年历史的查询。然而,为了公众利益,让我在这里提一下。由于以下声明,Spring 正在使用 CGLIB。
<!-- 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>
The property should be set to false, so that the CGLIB is not triggered instead JDK Dynamic Proxying.
该属性应设置为 false,以便不会触发 CGLIB 而不是 JDK 动态代理。
<property name="proxyTargetClass" value="false"/>
Hope that helps.
希望有帮助。