Spring的声明式事务底层原理
⽂章⽬录
声明式事务的概述
Spring 的声明式事务管理在底层是建⽴在 AOP 的基础之上的。其本质是对⽅法前后进⾏拦截,然后在⽬标⽅法开始之前创建或者加⼊⼀个事务,在执⾏完⽬标⽅法之后根据执⾏情况提交或者回滚事务。
声明式事务最⼤的优点就是不需要通过编程的⽅式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置⽂件中做相关的事务规则声明(或通过等价的基于标注的⽅式),便可以将事务规则应⽤到业务逻辑中。因为事务管理本⾝就是⼀个典型的横切逻辑,正是 AOP 的⽤武之地。Spring 开发团队也意识到了这⼀点,为声明式事务提供了简单⽽强⼤的⽀持。
在开发中使⽤声明式事务,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极⼤⽅便后期的代码维护。
和编程式事务相⽐,声明式事务唯⼀不⾜地⽅是,后者的最细粒度只能作⽤到⽅法级别,⽆法做到像编程式事务那样可以作⽤到代码块级别。但是即便有这样的需求,也存在很多变通的⽅法,⽐如,可以将需要进⾏事务管理的代码块独⽴为⽅法等等。
声明式事务管理也有两种常⽤的⽅式:
基于<tx>和<aop>命名空间的xml配置⽂件;
基于@Transactional注解。
声明式事务的初探
spring容器对外提供了了⼀个接⼝PlatformTransaction,其中声明了事务操作有关的⼏个⽅法,然后⼜定义了⼀个抽象类AbstractPlatformTransaction,这个抽象类是实现了接⼝PlatformTransaction,从⽽实现接⼝中的⽅法,以及具体的有关的事务操作了,不同的技术对应不同的实现,JDBC技术则对应⼀个DataSourceTransactionManager、Hibernater技术对应HibernateTransactionManager等等。
设计接⼝的原因
在这设计⼀个接⼝就可以让我们在客户端编程的时候不⽤关系采⽤的是什么技术,例⽤接⼝来定义⼀个参数,来控制事务,避免采和具体的技术相关,看到这也许会有疑问。我们不关⼼具体的技术,那么Spring容器到底采⽤哪种事务处理技术呢?JDBC的还是Hibernate的?这其实需要我们在配置⽂件中引⼊即可。
如果使⽤JDBC技术,引⼊事务管理器的时候就采⽤了DataSourceTransactionManager,如果我们使⽤的是Hibernate,则在配置⽂件中需要配置HibernateTransactionManager。这样就真正做到了⾯向接⼝编程。
设计抽象类的原因
在不同的技术实现中的commit操作和rollback操作是⼀样的,开启事务是不⼀样的。不同的技术有了相同的⾏为,我们就需要考虑抽象类的作⽤了,其实在Java⾯向对象的实现中抽象类的出现就是解决相同⾏为的问题,因为在抽象类中我们可以实现相同的⾏为,然后不同的⾏为继承这个抽象类然后重写不同的⾏为,这就是这个架构的精髓所在。这我们既做到了⾯向接⼝编程,有抽象出了共同的⾏为。
这个架构处处体现了模板模式,spring已经将模板的⾻架设计好了,我们只需要按照这个框架套进去需要的代码即可,当然在事务管理器的引⼊也存在着很多的技术,spring的强⼤之处就是把⾮常复杂的事情封装的⾮常简单,我们只需要简单的配置⼀下就可以实现。
声明式事务的源码分析
声明式事务结合IoC容器和Spirng已有的FactoryBean来对事务管理进⾏属性配置,⽐如传播⾏为,隔离级别等。其中最简单的⽅式就是通过配置TransactionProxyFactoryBean来实现声明式事务。
在整个源代码分析中,我们可以⼤致可以看到Spring实现声明式事物管理有这么⼏个部分:
对在上下⽂中配置的属性的处理,这⾥涉及的类是TransactionAttributeSourceAdvisor,这是⼀个通知器,⽤它来对属性值进⾏处理,属性信息放在TransactionAttribute中来使⽤,⽽这些属性的处理往往是和对切⼊点的处理是结合起来的。对属性的处理放在类TransactionAttributeSource中完成。
创建事物的过程,这个过程是委托给具体的事物管理器来创建的,但Spring通过TransactionStatus来传递相关的信息。
对事物的处理通过对相关信息的判断来委托给具体的事物管理器完成。
我们下⾯看看具体的实现,在TransactionFactoryBean中:
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
implements FactoryBean, BeanFactoryAware {
//这⾥是Spring事务处理⽽使⽤的AOP,中间封装了Spring对事务处理的代码来⽀持声明式事务处理的实现
private final TransactionInterceptor transactionInterceptor =new TransactionInterceptor();
private Pointcut pointcut;
//这⾥Spring把TransactionManager注⼊到TransactionInterceptor中去
public void setTransactionManager(PlatformTransactionManager transactionManager){
}
//这⾥把在bean配置⽂件中读到的事务管理的属性信息注⼊到TransactionInterceptor中去
public void setTransactionAttributes(Properties transactionAttributes){
}
.........中间省略了其他⼀些⽅法.......
//这⾥创建Spring AOP对事务处理的Advisor
protected Object createMainInterceptor(){
if(this.pointcut !=null){
//这⾥使⽤默认的通知器
return new DefaultPointcutAdvisor(this.ansactionInterceptor);
}
else{
// 使⽤上⾯定义好的TransactionInterceptor作为,同时使⽤TransactionAttributeSourceAdvisor
return new ansactionInterceptor);
}
}
}
那什么时候Spring的TransactionInterceptor被注⼊到Spring AOP中成为Advisor中的⼀部分呢?我们看到在TransactionProxyFactoryBean中,这个⽅法在IOC初始化bean的时候被执⾏:
public void afterPropertiesSet(){
.......
//TransactionProxyFactoryBean实际上使⽤ProxyFactory完成AOP的基本功能。
ProxyFactory proxyFactory =new ProxyFactory();
if(this.preInterceptors !=null){
for(int i =0; i <this.preInterceptors.length; i++){
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));
}
}
spring ioc注解//这⾥是Spring加⼊通知器的地⽅
//有两种通知器可以被加⼊DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor
//这⾥把Spring处理声明式事务处理的AOP代码都放到ProxyFactory中去,怎样加⼊advisor我们可以参考ProxyFactory的⽗类AdvisedSupport() //由它来维护⼀个advice的链表,通过这个链表的增删改来抽象我们对整个通知器配置的增删改操作。
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
if(this.postInterceptors !=null){
for(int i =0; i <this.postInterceptors.length; i++){
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));
}
}
//这⾥创建AOP的⽬标源
TargetSource targetSource =createTargetSource(this.target);
proxyFactory.setTargetSource(targetSource);
if(this.proxyInterfaces !=null){
proxyFactory.setInterfaces(this.proxyInterfaces);
}
else if(!isProxyTargetClass()){
proxyFactory.TargetClass()));
}
this.proxy =getProxy(proxyFactory);
}
Spring 已经定义了⼀个transctionInterceptor作为或者AOP advice的实现,在IOC容器中定义的其他属性⽐如transactionManager和事务管理的属性都会传到已经定义好的 TransactionInterceptor那⾥去进⾏处理。以上反映了基本的Spring AOP 的定义过程,其中pointcut和advice都已经定义好,同时也通过通知器配置到ProxyFactory中去了。
下⾯让我们回到TransactionProxyFactoryBean中看看TransactionAttributeSourceAdvisor是怎样定义的,这样我们可以理解具体的属性是怎样起作⽤,这⾥我们分析⼀下类TransactionAttributeSourceAdvisor:
public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
//和其他Advisor⼀样,同样需要定义AOP中的⽤到的Interceptor和Pointcut
//Interceptor使⽤传进来的TransactionInterceptor
//⽽对于pointcut,这⾥定义了⼀个内部类,参见下⾯的代码
private TransactionInterceptor transactionInterceptor;
private final TransactionAttributeSourcePointcut pointcut =new TransactionAttributeSourcePointcut();
.........
//定义的PointCut内部类
private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
.......
//⽅法匹配的实现,使⽤了TransactionAttributeSource类
public boolean matches(Method method, Class targetClass){
TransactionAttributeSource tas =getTransactionAttributeSource();
//这⾥使⽤TransactionAttributeSource来对配置属性进⾏处理
return(tas !=null&& TransactionAttribute(method, targetClass)!=null);
}
........省略了equal,hashcode,tostring的代码
}
这⾥我们看看属性值是怎样被读⼊的:AbstractFallbackTransactionAttributeSource负责具体的属性读⼊任务,我们可以有两种读⼊⽅式,⽐如annotation和直接配置.我们下⾯看看直接配置的读⼊⽅式,在Spring中同时对读⼊的属性值进⾏了缓存处理,这是⼀个decorator模式:
public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass){
//这⾥先查⼀下缓存⾥有没有事务管理的属性配置,如果有从缓存中取得TransactionAttribute
Object cacheKey =getCacheKey(method, targetClass);
Object cached =(cacheKey);
if(cached !=null){
if(cached ==NULL_TRANSACTION_ATTRIBUTE){
return null;
}
else{
return(TransactionAttribute) cached;
}
}
else{
// 这⾥通过对⽅法和⽬标对象的信息来计算事务缓存属性
TransactionAttribute txAtt =computeTransactionAttribute(method, targetClass);
//把得到的事务缓存属性存到缓存中,下次可以直接从缓存中取得。
if(txAtt ==null){
this.cache.put(cacheKey,NULL_TRANSACTION_ATTRIBUTE);
}
else{
...........
this.cache.put(cacheKey, txAtt);
}
return txAtt;
}
}
基本的处理在computeTransactionAttribute()中:
private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass){
//这⾥检测是不是public⽅法
if(allowPublicMethodsOnly()&&!Modifier.Modifiers())){
return null;
}
Method specificMethod = MostSpecificMethod(method, targetClass);
// First try is the method in the target class.
TransactionAttribute txAtt =findTransactionAttribute(findAllAttributes(specificMethod));
if(txAtt !=null){
return txAtt;
}
// Second try is the transaction attribute on the target class.
txAtt =findTransactionAttribute(DeclaringClass()));
if(txAtt !=null){
return txAtt;
}
if(specificMethod != method){
// Fallback is to look at the original method.
txAtt =findTransactionAttribute(findAllAttributes(method));
if(txAtt !=null){
return txAtt;
}
// Last fallback is the class of the original method.
return findTransactionAttribute(DeclaringClass()));
}
return null;
}
经过⼀系列的尝试我们可以通过findTransactionAttribute()通过调⽤findAllAttribute()得到TransactionAttribute的对象,如果返回的是null,这说明该⽅法不是我们需要事务处理的⽅法。
在完成把需要的通知器加到ProxyFactory中去的基础上,我们看看具体的看事务处理代码怎样起作⽤,在TransactionInterceptor中:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论