轉(zhuǎn)自CSDN 原文鏈接
前些日子一朋友在需要在目標對象中進行自我調(diào)用,且需要實施相應的事務定義霎肯,且網(wǎng)上的一種通過BeanPostProcessor的解決方案是存在問題的。因此專門寫此篇帖子分析why榛斯。
1观游、預備知識
aop概念請參考【http://www.iteye.com/topic/1122401】和【http://jinnianshilongnian.iteye.com/blog/1418596】
spring的事務管理,請參考【http://jinnianshilongnian.iteye.com/blog/1441271】
使用AOP 代理后的方法調(diào)用執(zhí)行流程驮俗,如圖所示
也就是說我們首先調(diào)用的是AOP代理對象而不是目標對象懂缕,首先執(zhí)行事務切面,事務切面內(nèi)部通過TransactionInterceptor環(huán)繞增強進行事務的增強王凑,即進入目標方法之前開啟事務搪柑,退出目標方法時提交/回滾事務。
2索烹、測試代碼準備
Java代碼
public interface AService {
public void a();
public void b();
}
@Service()
public class AServiceImpl1 implements AService{
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
this.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
3工碾、問題
目標對象內(nèi)部的自我調(diào)用將無法實施切面中的增強,如圖所示
此處的this指向目標對象百姓,因此調(diào)用this.b()將不會執(zhí)行b事務切面渊额,即不會執(zhí)行事務增強,因此b方法的事務定義“@Transactional(propagation = Propagation.REQUIRES_NEW)”將不會實施瓣戚,即結(jié)果是b和a方法的事務定義是一樣的端圈,可以從以下日志看出:
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'a' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager'
org.springframework.orm.hibernate4.HibernateTransactionManager Creating new transaction with name [com.sishuok.service.impl.AServiceImpl1.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' -----創(chuàng)建a方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session …… for Hibernate transaction ---打開Session
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl1.a]
org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl1.a] ----完成a方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit
org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session ……---提交a方法事務
或
org.springframework.orm.hibernate4.HibernateTransactionManager Rolling back Hibernate transaction on Session ……---如果有異常將回滾a方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization
org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization
……
org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session …… after transaction --關閉Session
我們可以看到事務切面只對a方法進行了事務增強焦读,沒有對b方法進行增強子库。
3、解決方案
此處a方法中調(diào)用b方法時矗晃,只要通過AOP代理調(diào)用b方法即可走事務切面仑嗅,即可以進行事務增強,如下所示:
Java代碼
public void a() {
aopProxy.b();//即調(diào)用AOP代理對象的b方法即可執(zhí)行事務切面進行事務增強
}
判斷一個Bean是否是AOP代理對象可以使用如下三種方法:
AopUtils.isAopProxy(bean) : 是否是代理對象;
AopUtils.isCglibProxy(bean) : 是否是CGLIB方式的代理對象仓技;
AopUtils.isJdkDynamicProxy(bean) : 是否是JDK動態(tài)代理方式的代理對象鸵贬;
3.1、通過ThreadLocal暴露Aop代理對象
1脖捻、開啟暴露Aop代理到ThreadLocal支持(如下配置方式從spring3開始支持)
<aop:aspectj-autoproxy expose-proxy="true"/><!—注解風格支持-->
<aop:config expose-proxy="true"><!—xml風格支持-->
2阔逼、修改我們的業(yè)務實現(xiàn)類
this.b();-----------修改為--------->((AService) AopContext.currentProxy()).b();
3、執(zhí)行測試用例地沮,日志如下
org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager'
org.springframework.orm.hibernate4.HibernateTransactionManager Creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' -----創(chuàng)建a方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session ……for Hibernate transaction --打開a Session
org.springframework.orm.hibernate4.HibernateTransactionManager Preparing JDBC Connection of Hibernate Session ……
org.springframework.orm.hibernate4.HibernateTransactionManager Exposing Hibernate transaction as JDBC transaction ……
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.a]
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'b' with attribute: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
……
org.springframework.orm.hibernate4.HibernateTransactionManager Suspending current transaction, creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.b] -----創(chuàng)建b方法事務(并暫停a方法事務)
……
org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session for Hibernate transaction ---打開b Session
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.b]
org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl2.b] ----完成b方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit
org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session …… ---提交b方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization
org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization
……
org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session …… after transaction --關閉 b Session
-----到此b方法事務完畢
org.springframework.orm.hibernate4.HibernateTransactionManager Resuming suspended transaction after completion of inner transaction ---恢復a方法事務
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl2.a] ----完成a方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit
org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session ……---提交a方法事務
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization
org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization
……
org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session …… after transaction --關閉 a Session
此處我們可以看到b方法的事務起作用了嗜浮。
以上方式是解決目標對象內(nèi)部方法自我調(diào)用并實施事務的最簡單的解決方案。
4摩疑、實現(xiàn)原理分析
4.1危融、在進入代理對象之后通過AopContext.serCurrentProxy(proxy)暴露當前代理對象到ThreadLocal,并保存上次ThreadLocal綁定的代理對象為oldProxy雷袋;
4.2吉殃、接下來我們可以通過 AopContext.currentProxy() 獲取當前代理對象;
4.3楷怒、在退出代理對象之前要重新將ThreadLocal綁定的代理對象設置為上一次的代理對象蛋勺,即AopContext.serCurrentProxy(oldProxy)。
有些人不喜歡這種方式鸠删,說通過ThreadLocal暴露有性能問題迫卢,其實這個不需要考慮,因為事務相關的(Session和Connection)內(nèi)部也是通過SessionHolder和ConnectionHolder暴露到ThreadLocal實現(xiàn)的冶共。
不過自我調(diào)用這種場景確實只有很少情況遇到乾蛤,因此不用這種方式我們也可以通過如下方式實現(xiàn)。
3.2捅僵、通過初始化方法在目標對象中注入代理對象
Java代碼
@Service
public class AServiceImpl3 implements AService{
@Autowired //① 注入上下文
private ApplicationContext context;
private AService proxySelf; //② 表示代理對象家卖,不是目標對象
@PostConstruct //③ 初始化方法
private void setSelf() {
//從上下文獲取代理對象(如果通過proxtSelf=this是不對的,this是目標對象)
//此種方法不適合于prototype Bean庙楚,因為每次getBean返回一個新的Bean
proxySelf = context.getBean(AService.class);
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
proxySelf.b(); //④ 調(diào)用代理對象的方法 這樣可以執(zhí)行事務切面
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
此處日志就不分析上荡,和3.1類似。此種方式不是很靈活馒闷,所有需要自我調(diào)用的實現(xiàn)類必須重復實現(xiàn)代碼酪捡。
3.3、通過BeanPostProcessor在目標對象中注入代理對象
此種解決方案可以參考http://fyting.iteye.com/blog/109236纳账。
BeanPostProcessor 的介紹和使用敬請等待我的下一篇分析帖逛薇。
一、定義BeanPostProcessor 需要使用的標識接口
Java代碼
public interface BeanSelfAware {
void setSelf(Object proxyBean);
}
即我們自定義的BeanPostProcessor (InjectBeanSelfProcessor)如果發(fā)現(xiàn)我們的Bean是實現(xiàn)了該標識接口就調(diào)用setSelf注入代理對象疏虫。
二永罚、Bean實現(xiàn)
Java代碼
@Service
public class AServiceImpl4 implements AService, BeanSelfAware {//此處省略接口定義
private AService proxySelf;
public void setSelf(Object proxyBean) { //通過InjectBeanSelfProcessor注入自己(目標對象)的AOP代理對象
this.proxySelf = (AService) proxyBean;
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
proxySelf.b();//調(diào)用代理對象的方法 這樣可以執(zhí)行事務切面
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
實現(xiàn)BeanSelfAware標識接口的setSelf將代理對象注入啤呼,并且通過“proxySelf.b()”這樣可以實施b方法的事務定義。
三呢袱、InjectBeanSelfProcessor實現(xiàn)
Java代碼
@Component
public class InjectBeanSelfProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof BeanSelfAware) {//如果Bean實現(xiàn)了BeanSelfAware標識接口官扣,就將代理對象注入
((BeanSelfAware) bean).setSelf(bean); //即使是prototype Bean也可以使用此種方式
}
return bean;
}
}
postProcessAfterInitialization根據(jù)目標對象是否實現(xiàn)BeanSelfAware標識接口,通過setSelf(bean)將代理對象(bean)注入到目標對象中羞福,從而可以完成目標對象內(nèi)部的自我調(diào)用惕蹄。
關于BeanPostProcessor的執(zhí)行流程等請一定參考我的這篇帖子,否則無法繼續(xù)往下執(zhí)行治专。
四焊唬、InjectBeanSelfProcessor的問題
(1、場景:通過InjectBeanSelfProcessor進行注入代理對象且循環(huán)依賴場景下會產(chǎn)生前者無法通過setSelf設置代理對象的問題看靠。 循環(huán)依賴是應該避免的赶促,但是實際工作中不可避免會有人使用這種注入,畢竟沒有強制性挟炬。
(2鸥滨、用例
(2.1、定義BeanPostProcessor 需要使用的標識接口
和3.1中一樣此處不再重復谤祖。
(2.2婿滓、Bean實現(xiàn)
Java代碼
@Service
public class AServiceImpl implements AService, BeanSelfAware {//此處省略Aservice接口定義
@Autowired
private BService bService; //① 通過@Autowired方式注入BService
private AService self; //② 注入自己的AOP代理對象
public void setSelf(Object proxyBean) {
this.self = (AService) proxyBean; //③ 通過InjectBeanSelfProcessor注入自己(目標對象)的AOP代理對象
System.out.println("AService=="+ AopUtils.isAopProxy(this.self)); //如果輸出true標識AOP代理對象注入成功
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
self.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
Java代碼
@Service
public class BServiceImpl implements BService, BeanSelfAware {//此處省略Aservice接口定義
@Autowired
private AService aService; //① 通過@Autowired方式注入AService
private BService self; //② 注入自己的AOP代理對象
public void setSelf(Object proxyBean) { //③ 通過InjectBeanSelfProcessor注入自己(目標對象)的AOP代理對象
this.self = (BService) proxyBean;
System.out.println("BService=" + AopUtils.isAopProxy(this.self)); //如果輸出true標識AOP代理對象注入成功
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
self.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
此處A依賴B,B依賴A粥喜,即構(gòu)成循環(huán)依賴凸主,此處不探討循環(huán)依賴的設計問題(實際工作應該避免循環(huán)依賴),只探討為什么循環(huán)依賴會出現(xiàn)注入代理對象失敗的問題额湘。
循環(huán)依賴請參考我的博文http://jinnianshilongnian.iteye.com/blog/1415278卿吐。
依賴的初始化和銷毀順序請參考我的博文http://jinnianshilongnian.iteye.com/blog/1415461。
(2.3锋华、InjectBeanSelfProcessor實現(xiàn)
和之前3.3中一樣 此處不再重復嗡官。
(2.4、測試用例
Java代碼
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:spring-config.xml"})
public class SelfInjectTest {
@Autowired
AService aService;
@Autowired
BService bService;
@Test
public void test() {
}
}
執(zhí)行如上測試用例會輸出:
BService=true
AService==false
即BService通過InjectBeanSelfProcessor注入代理對象成功毯焕,而AService卻失敗了(實際是注入了目標對象)衍腥,如下是debug得到的信息:
(2. 5、這是為什么呢纳猫,怎么在循環(huán)依賴會出現(xiàn)這種情況婆咸?
敬請期待我的下一篇分析帖。
3.4芜辕、改進版的InjectBeanSelfProcessor的解決方案
Java代碼
@Component
public class InjectBeanSelfProcessor2 implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext context;
//① 注入ApplicationContext
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(!(bean instanceof BeanSelfAware)) { //② 如果Bean沒有實現(xiàn)BeanSelfAware標識接口 跳過
return bean;
}
if(AopUtils.isAopProxy(bean)) { //③ 如果當前對象是AOP代理對象尚骄,直接注入
((BeanSelfAware) bean).setSelf(bean);
} else {
//④ 如果當前對象不是AOP代理,則通過context.getBean(beanName)獲取代理對象并注入
//此種方式不適合解決prototype Bean的代理對象注入
((BeanSelfAware)bean).setSelf(context.getBean(beanName));
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
5物遇、總結(jié)
縱觀其上:
【3.1 通過ThreadLocal暴露Aop代理對象】適合解決所有場景(不管是singleton Bean還是prototype Bean)的AOP代理獲取問題(即能解決目標對象的自我調(diào)用問題)乖仇;
【3.2 通過初始化方法在目標對象中注入代理對象】 和【3.4 改進版的InjectBeanSelfProcessor的解決方案】能解決普通(無循環(huán)依賴)的AOP代理對象注入問題憾儒,而且也能解決【3.3】中提到的循環(huán)依賴(應該是singleton之間的循環(huán)依賴)造成的目標對象無法注入AOP代理對象問題询兴,但該解決方案不適合解決循環(huán)依賴中包含prototype Bean的自我調(diào)用問題乃沙;
【3.3 通過BeanPostProcessor 在目標對象中注入代理對象】:只能解決 普通(無循環(huán)依賴)的 的Bean注入AOP代理,無法解決循環(huán)依賴的AOP代理對象注入問題诗舰,即無法解決目標對象的自我調(diào)用問題警儒。
jingnianshilongnian 寫道
spring允許的循環(huán)依賴(只考慮單例和原型Bean):
A----B
B----A
只有在A和B都不為原型是允許的,即如果A和B都是prototype則會報錯(無法進行原型Bean的循環(huán)依賴)眶根。
A(單例)---B(單例) 或 A(原型)---B(單例) 這是可以的蜀铲,但 A(原型)---B(原型)或 A(原型)---B(單例Lazy)【且context.getBean("A")】時 這是不允許的。
一属百、A(原型)---B(原型) A(原型)---B(單例Lazy)【且context.getBean("A")】 會報:
Error creating bean with name 'BServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.sishuok.issue.AService com.sishuok.issue.impl.BServiceImpl.aService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'AServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.sishuok.issue.BService com.sishuok.issue.impl.AServiceImpl.bService; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'BServiceImpl': Requested bean is currently in creation: [color=red]Is there an unresolvable circular reference[/color]?
二记劝、A(原型)---B(單例) 和 A(單例)---B(單例)
這種方式 使用我的 【3.3 通過BeanPostProcessor 在目標對象中注入代理對象】 是沒有問題的。
因此【 3.4 改進版的InjectBeanSelfProcessor的解決方案 】 可以作為最后的解決方案族扰。