Spring 版本:3.2.x, 4.0.x
【問題說明】
首先介紹下配置出錯情況:
(1)項目中,Spring3 and Spring4 的 applicationContext.xml aop 配置如下:
... ... <aop:aspectj-autoproxy expose-proxy="true"/> <tx:annotation-driven transaction-manager="transactionManager"/> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="create*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="del*" propagation="REQUIRED" /> <tx:method name="get*" propagation="REQUIRED" read-only="true" /> <tx:method name="find*" propagation="REQUIRED" read-only="true" /> <tx:method name="query*" propagation="REQUIRED" read-only="true" /> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> <aop:config expose-proxy="true"> <!-- 只對業務邏輯層實施事務 --> <aop:pointcut id="txPointcut" expression="execution(* com.app..service..*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config>... ...
未加入 Shiro 之前,所有 applicationContext 的 getBean(...) 方法能正常獲取到相應對象實例。 調用getBeanDefinitionNames()輸出的 Beans 如下:
Bean Name | Bean Class |
userDaoImpl | class com.app.dao.impl.UserDaoImpl |
resourceServiceImpl | class com.app.service.impl.ResourceServiceImpl$$EnhancerBySpringCGLIB$$4ddf6901 |
roleServiceImpl | class com.app.service.impl.RoleServiceImpl |
userServiceImpl | class com.app.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$e7bcd944 |
以上 bean 都采用 @Repository 或@Service 的注解方式。
注意標紅Class名稱,這是因為被 CGLIB 動態代理。
(2)根據 Shiro 示例,新增配置文件 applicationContext-security.xml,然后配置Shiro 如下:
... ... <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm"/> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"/>... ...
加入 Shiro 之后,發現有些地方 getBean(...) 出錯,調用getBeanDefinitionNames()輸出的 Beans 如下:
Bean Name | Bean Class |
userDaoImpl | class com.app.dao.impl.UserDaoImpl |
resourceServiceImpl | class $Proxy38 |
roleServiceImpl | class com.app.service.impl.RoleServiceImpl |
userServiceImpl | class $Proxy32 |
可以注意到標紅Class名稱,這是由于配置 DefaultAdvisorAutoProxyCreator,又被 JDK 動態二次代理。
【解決辦法】
兩次都以 CGLIB 動態代理。在 AOP 配置加上proxy-target-class 屬性,值為 true。具體如下:
(1)在 applicationContext.xml 中配置
... ... <aop:config expose-proxy="true" proxy-target-class="true"> <!-- 只對業務邏輯層實施事務 --> <aop:pointcut id="txPointcut" expression="execution(* com.app..service..*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config>... ...
(2)在 applicationContext-security.xml 配置 Shiro
... ... <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true"/> </bean>... ...
修改完成后,重新運行程序,getBean 恢復正常。
【參考資料】
spring的二次代理原因及如何排查 http://jinnianshilongnian.VEvb.com/blog/1894465
新聞熱點
疑難解答