0

spring事务小结

 2 years ago
source link: https://qiankunli.github.io/2017/07/26/spring_tx.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文基本算第19章 Spring事务王国的架构的重新整理。

Spring事务分析(2)–基于声明式的事务管理实现分析对源码的分析也比较好,注意学习其分析源码的方式:通过画UML图来分析,更重要的是,这些UML图位于逻辑主线的关键节点上。

Transaction Management 官方文档不可不看,几张图还是非常精到的。

spring 事务重点就两块:

  1. 通过 TransactionDefinition、PlatformTransactionManager、TransactionStatus 提供基本抽象
  2. 通过代理模式将第一点提到的抽象加入到业务代码中,通过注解简化编程式事务代码。代码技巧上小结一下就是:通过注解隐藏代理模式

先从编程式事务说起

public void serviceMethod(){  
    TransactionDefinition definition = ...;  
    PlatformTransactionManager transactionManager = ...;
    TransactionStatus txStatus = transactionManager.getTransaction(definition);  
    try {  
        // dao1.doDataAccess();  
        // dao2.doDataAccess();  
        // ...  
    }catch(DataAccessException e){  
        transactionManager.rollback(txStatus);  
        throw e;  
    }catch(OtherNecessaryException e){  
        transactionManager.rollback(txStatus);  
        throw e;  
    }  
    transactionManager.commit(txStatus);  
}  

TransactionDefinition、PlatformTransactionManager、TransactionStatus是spring tx提供的三个基本抽象。

Spring的事务框架设计理念的基本原则是:让事务管理的关注点与数据访问关注点相分离。如上图,try代码块数据还按照原来的方式访问,不管是datasource、hibernate还是其他数据源,亦或是rpc服务,怎么变化,try代码块之外的事务代码可以岿然不动。

通过注解简化编程式事务代码

声明式事务,如何从代码上将事务与数据访问分离?

从常规路线来说,一般从配置解析开始,spring tx将配置存到一些数据类中。

<!-- 声明事务管理器,并支持事务注解 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />

宏观上,spring aop 一般涉及到ProxyFactoryBean,spring启动时,识别标记有@transactional的方法的类,并将其关联到TransactionProxyFactoryBean。使得 ioc 返回带@Transactional 方法的类的实例时,返回的是代理类实例。

如何识别@transactional?

  1. 对于spring aop, spring源码分析之——spring aop原理 从代码上看,Spring AOP 实现一个InstantiationAwareBeanPostProcessor接口的bean。在每次bean初始化的时候找到所有advisor(spring ioc启动时,会采集类信息存储在BeanDefinition中),根据pointcut 判断是不是需要为将实例化的bean生成代理,如果需要,就把advice编制在代理对象里面。

    而spring tx 在解析<tx:annotation-driven transaction-manager="txManager" />会向ioc 注册 advisor 的BeanDefinition。也就是说,使用spring aop,额外支持一个注解时,向ioc注册一个advisor的BeanDefinition即可。当然,自己定义BeanPostProcessor,直接处理自定义注解也是可以的。

  2. 对于aspectj,定义切点就比较直接了。

     public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {
         private pointcut executionOfTransactionalMethod() :execution(@Transactional * *(..));
     }

TransactionProxyFactoryBean 抛开ProxyFactoryBean功能,其功能点转向了其成员TransactionInterceptor,TransactionInterceptor实现了MethodInterceptor。

public interface MethodInterceptor extends Interceptor {
	/**
 	* Implement this method to perform extra treatments before and
 	* after the invocation.
 	* /
	Object invoke(MethodInvocation invocation) throws Throwable;
}

其invoke方法,在实际方法invocation 执行的前后,做了事务相关的处理。基本套路就是构造一个大杂烩的”能力对象”TransactionInfo,然后在各个场景应用它。逻辑比我们想的复杂,用于实现各种”传播”行为。

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
	retVal = invocation.proceedWithInvocation();
}catch (Throwable ex) {
	completeTransactionAfterThrowing(txInfo, ex);
	throw ex;
}finally {
	cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
TransactionAspectSupport==> PlatformTransactionManager==> 子类 含义
涉及到的对象 TransactionInfo TransactionStatus、TransactionAttribute Object TransactionStatus是一系列状态对象组成的聚合类;到子类时,则不用返回TransactionStatus,只需返回其中一个transaction(Object类型)成员即可
方法 invokeWithinTransaction TransactionStatus getTransaction(TransactionDefinition definition); Object doGetTransaction() 根据配置数据获取当前状态
    void commit(TransactionStatus status void doCommit(DefaultTransactionStatus status)  
    void rollback(TransactionStatus status) void doRollback(DefaultTransactionStatus status)  

从某种程度上,transactionManager子类方法跟servlet的init、doGet、destroy作用是一样的。

2018.01.07 补充spring 官方图

实现一个简单的PlatformTransactionManager

PlatformTransactionManager是整个事务抽象策略的顶层接口,它就好像我们的战略蓝图,而战略的具体实施则将由相应的PlatformTransactionManager实现类来执行。

以JDBC数据访问方式的局部事务管理为例,统一中原的过程(1),其基本原理是:保证两个dao的数据访问方法使用的是同一个java.sql.Connection,这样PlatformTransactionManager的commit和rollback就可以转化为conn的对应方法。

采用称为connection-passing的方式,org.springframework.jdbc.datasource.DataSourceTransactionManager在事务开始时,获取一个conneciton,将其挂到当前线程,随后dao层在获取connection时,先尝试从当前线程获取Connection,(无则新建),然后使用Connection进行数据操作。可以看出,Spring的事务管理与它的数据访问框架是紧密结合的。

事务中的一些概念

TransactionDefinition(1)

对于TransactionDefinition的命名,可以联系spring ioc中的BeanDefinition

  1. 事务的隔离级别,并发事务的相互影响
  2. 事务的传播行为,直接说,就是一个标记了@Transactional的方法,调用了另一个标记了@Transactional的方法。后一个方法的失败是否会影响前一个方法。
  3. 事务的超时时间
  4. 是否为只读事务

事务实现的其它问题

  1. 事务数据的持久化。mysql 有redo 和undo log。因此mysql的关闭不影响事务,但业务系统因为更新代码等原因,经常要重新启动,若不能在重启启动时恢复或回滚事务,则会对系统的一致性造成比较大的影响。changmingxie/compensable-transaction 中包含的了数据的存储逻辑。

一个@Transactional 失效问题的排查

有一个看到一个@Transactional 失效的问题,并且奇怪的是本地测试生效,测试环境不生效。我们知道@Transactional 的实现原理是

  1. 对于@Transactional 注解方法的两次jdbc 操作
  2. 先尝试从ThreadLocal 中获取数据库连接,若有,则使用该数据库连接。若没有,则向数据库连接池申请。以确保两次jdbc 操作均使用同一个数据库连接。

当时实在是没办法了,便将spring-tx 包的日志放开,想看下 数据库连接的申请和释放过程,以确保两次操作用的是同一个数据库连接。但从日志看,事务管理器生效了,但管控的不是jdbc 操作的数据库连接,为何呢?<tx:annotation-driven /> 在项目代码中配置了多个,项目用到了两个不同的db,包含在不同的spring jdbc context文件中,且本地环境和测试环境配置文件中spring jdbc context 引入的顺序不同。实际运行时,第一个配置的<tx:annotation-driven /> 生效。

通过对spring事务的分析,可以学到:

  1. 自定义<tx:annotation-driven transaction-manager="txManager" />表现,将其转化特定的数据结构TransactionDefinition及其TransactionAttribute
  2. 通过aspectj或spring aop,通过注解的方式与现有代码协作,囊括前置、后置、切面、异常处理等所有可能

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK