Spring事务处理时自我调用的解决方案及一些实现方式的风险

标签:
spring传播机制内部调用事务处理隔离级别 |
分类: 框架 |
转:http://www.iteye.com/topic/1122740
前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的。因此专门写此篇帖子分析why。
1、预备知识
aop概念请参考【http://www.iteye.com/topic/1122401】和【http://jinnianshilongnian.iteye.com/blog/1418596】
spring的事务管理,请参考【http://jinnianshilongnian.iteye.com/blog/1441271】
使用AOP
也就是说我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。
2、测试代码准备
-
public
interface AService { -
public void a(); -
public void b(); -
}
-
-
@Service()
-
public
class AServiceImpl1 implementsAService{ -
@Transactional(propagation = Propagation.REQUIRED) -
public void a() { -
this.b(); -
} -
@Transactional(propagation = Propagation.REQUIRES_NEW) -
public void b() { -
} -
}
3、问题
目标对象内部的自我调用将无法实施切面中的增强,如图所示
http://img.blog.csdn.net/20140529203701328?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3h3enk3Mzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
此处的this指向目标对象,因此调用this.b()将不会执行b事务切面,即不会执行事务增强,因此b方法的事务定义“@Transactional(propagation = Propagation.REQUIRES_NEW)”将不会实施,即结果是b和a方法的事务定义是一样的,可以从以下日志看出:
org.springframework.beans.factory.support.DefaultListableBeanFacto
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
……
org.springframework.transaction.support.TransactionSynchronizati
org.springframework.transaction.interceptor.TransactionInterceptor
org.springframework.transaction.interceptor.TransactionInterceptor
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
或
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.transaction.support.TransactionSynchronizati
……
org.springframework.orm.hibernate4.HibernateTransactionMana
我们可以看到事务切面只对a方法进行了事务增强,没有对b方法进行增强。
3、解决方案
此处a方法中调用b方法时,只要通过AOP代理调用b方法即可走事务切面,即可以进行事务增强,如下所示:
-
public
void a() { -
aopProxy.b();//即调用AOP代理对象的b方法即可执行事务切面进行事务增强
-
}
判断一个Bean是否是AOP代理对象可以使用如下三种方法:
AopUtils.isAopProxy(bean)
AopUtils.isCglibProxy(bean)
AopUtils.isJdkDynamicProxy(bean)
3.1、通过ThreadLocal暴露Aop代理对象
1、开启暴露Aop代理到ThreadLocal支持(如下配置方式从spring3开始支持)
-
<</span>aop:aspectj-autoproxy
expose-proxy="true"/>> -
<</span>aop:config
expose-proxy="true">>
2、修改我们的业务实现类
this.b();-----------修改为--------->((AService) AopContext.currentProxy()).b();
3、执行测试用例,日志如下
org.springframework.beans.factory.support.DefaultListableBeanFacto
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
……
org.springframework.transaction.support.TransactionSynchronizati
org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.a]
org.springframework.transaction.annotation.AnnotationTransactionAtt
……
org.springframework.orm.hibernate4.HibernateTransactionMana
……
org.springframework.orm.hibernate4.HibernateTransactionMana
……
org.springframework.transaction.support.TransactionSynchronizati
org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.b]
org.springframework.transaction.interceptor.TransactionInterceptor
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.transaction.support.TransactionSynchronizati
……
org.springframework.orm.hibernate4.HibernateTransactionMana
-----到此b方法事务完毕
org.springframework.orm.hibernate4.HibernateTransactionMana
……
org.springframework.transaction.support.TransactionSynchronizati
org.springframework.transaction.interceptor.TransactionInterceptor
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.orm.hibernate4.HibernateTransactionMana
org.springframework.transaction.support.TransactionSynchronizati
……
org.springframework.orm.hibernate4.HibernateTransactionMana
此处我们可以看到b方法的事务起作用了。
以上方式是解决目标对象内部方法自我调用并实施事务的最简单的解决方案。
4、实现原理分析
http://img.blog.csdn.net/20140529203838781?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3h3enk3Mzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
4.1、在进入代理对象之后通过AopContext.setCurrentProxy(proxy)暴露当前代理对象到ThreadLocal,并保存上次ThreadLocal绑定的代理对象为oldProxy;
4.2、接下来我们可以通过
4.3、在退出代理对象之前要重新将ThreadLocal绑定的代理对象设置为上一次的代理对象,即AopContext.setCurrentProxy(oldProxy)。
有些人不喜欢这种方式,说通过ThreadLocal暴露有性能问题,其实这个不需要考虑,因为事务相关的(Session和Connection)内部也是通过SessionHolder和ConnectionHolder暴露到ThreadLocal实现的。
不过自我调用这种场景确实只有很少情况遇到,因此不用这种方式我们也可以通过如下方式实现。
3.2、通过初始化方法在目标对象中注入代理对象
-
@Service
-
public
class AServiceImpl3 implementsAService{ -
@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(); //④ 调用代理对象的方法 这样可以执行事务切面 -
} -
@Transactional(propagation = Propagation.REQUIRES_NEW) -
public void b() { -
} -
}
此处日志就不分析,和3.1类似。此种方式不是很灵活,所有需要自我调用的实现类必须重复实现代码。
3.3、通过BeanPostProcessor 在目标对象中注入代理对象
此种解决方案可以参考http://fyting.iteye.com/blog/109236。
BeanPostProcessor
一、定义BeanPostProcessor
-
public
interface BeanSelfAware { -
void setSelf(Object proxyBean); -
}
即我们自定义的BeanPostProcessor
二、Bean实现
-
@Service
-
public
class AServiceImpl4 implementsAService, //此处省略接口定义BeanSelfAware { -
private AService proxySelf; -
public void setSelf(Object //通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象proxyBean) { -
this.proxySelf = (AService) proxyBean; -
} -
@Transactional(propagation = Propagation.REQUIRED) -
public void a() { -
proxySelf.b();//调用代理对象的方法 这样可以执行事务切面 -
} -
@Transactional(propagation = Propagation.REQUIRES_NEW) -
public void b() { -
} -
}
实现BeanSelfAware标识接口的setSelf将代理对象注入,并且通过“proxySelf.b()”这样可以实施b方法的事务定义。
三、InjectBeanSelfProcessor实现
-
@Component
-
public
class InjectBeanSelfProcessor implementsBeanPostProcessor { -
public Object throwspostProcessBeforeInitial ization(Object bean, String beanName) BeansException { -
return bean; -
} -
public Object throwspostProcessAfterInitiali zation(Object bean, String beanName) BeansException { -
if(bean instanceof BeanSelfAware) //如果Bean实现了BeanSelfAware标识接口,就将代理对象注入{ -
((BeanSelfAware) bean).setSelf(bean); //即使是prototype Bean也可以使用此种方式 -
} -
return bean; -
} -
}
postProcessAfterInitiali
关于BeanPostProcessor的执行流程等请一定参考我的这篇帖子,否则无法继续往下执行。
四、InjectBeanSelfProcessor的问题
(1、场景:通过InjectBeanSelfProcessor进行注入代理对象且循环依赖场景下会产生前者无法通过setSelf设置代理对象的问题。
(2、用例
(2.1、定义BeanPostProcessor
和3.1中一样此处不再重复。
(2.2、Bean实现
-
@Service
-
public
class AServiceImpl implementsAService, //此处省略Aservice接口定义BeanSelfAware { -
@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() { -
} -
}
-
@Service
-
public
class BServiceImpl implementsBService, //此处省略Aservice接口定义BeanSelfAware { -
@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=" + this.self));AopUtils.isAopProxy( //如果输出true标识AOP代理对象注入成功 -
} -
@Transactional(propagation = Propagation.REQUIRED) -
public void a() { -
self.b(); -
} -
@Transactional(propagation = Propagation.REQUIRES_NEW) -
public void b() { -
} -
}
此处A依赖B,B依赖A,即构成循环依赖,此处不探讨循环依赖的设计问题(实际工作应该避免循环依赖),只探讨为什么循环依赖会出现注入代理对象失败的问题。
循环依赖请参考我的博文【http://jinnianshilongnian.iteye.com/blog/1415278】。
依赖的初始化和销毁顺序请参考我的博文【http://jinnianshilongnian.iteye.com/blog/1415461】。
(2.3、InjectBeanSelfProcessor实现
和之前3.3中一样
(2.4、测试用例
-
@RunWith(value
= class)SpringJUnit4ClassRunner. -
@ContextConfiguration(value
= "classpath:spring-config.xml"}){ -
public
class SelfInjectTest { -
@Autowired -
AService aService; -
@Autowired -
BService bService; -
@Test -
public void test() { -
} -
}
执行如上测试用例会输出:
BService=true
AService==false
即BService通过InjectBeanSelfProcessor注入代理对象成功,而AService却失败了(实际是注入了目标对象),如下是debug得到的信息:
http://img.blog.csdn.net/20140529204055312?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3h3enk3Mzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
(2. 5、这是为什么呢,怎么在循环依赖会出现这种情况?
敬请期待我的下一篇分析帖。
3.4、改进版的InjectBeanSelfProcessor的解决方案
-
@Component
-
public
class InjectBeanSelfProcessor2 implementsBeanPostProcessor, ApplicationContextAware { -
private ApplicationContext context; -
//① 注入ApplicationContext -
public void setApplicationContext(ApplicationContext throwsapplicationContext) BeansException { -
this.context = applicationContext; -
} -
public Object throwspostProcessAfterInitiali zation(Object bean, String beanName) BeansException { -
if(!(bean instanceof BeanSelfAware)) //②{ 如果Bean没有实现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 throwspostProcessBeforeInitial ization(Object bean, String beanName) BeansException { -
return bean; -
} -
}
5、总结
纵观其上:
【3.1
【3.2
【3.3