一篇很棒的博客文章,解释了优缺点JDK与CGLIB代理的对比.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.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.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 DataSourceTransactionManager instead 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?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 moreCaused 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 moreEDIT: 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-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 This is only included when running the unit tests. It's purpose is to overwrite some of the beans declared in the other config files.<?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> 解决方案 Sounds like you're referencing an implementation class instead of an interface. There is an excerpt here with more detail.A Spring forum post: "Mixing JDK and CGLIB proxies"A great blog post explaining pros and cons of JDK vs. CGLIB proxies. 这篇关于运行Spring单元测试的AOP问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 09-06 02:55