spring 事务原理_Spring源码分析深入了解事务管理的原理实现

一、前言

事务管理对于系统应用来说至关重要,它保证了数据的完整性和安全性。特别是针对金融服务而言,更是不可或缺。经典的场景是转账操作,A账户向B账户转账5000元,首先A余额减少5000元,然后B余额增加5000元。通常情况下,都能正常完成交易。但也难免会遇到故障,这时候不能出现A的余额减少了,B的余额却没有增加的情况。

在分析源码之前,我们先来了解下Spring中的一些事务属性。

126389bb780995aba9bcce26a7b5c676.png

二、事务属性

1、事务隔离级别

事务隔离级别,定义了一个事务可能受其他并发事务活动影响的程度。

在应用程序中,多个事务同时运行,经常会为了完成相同的工作而操作同一数据。并发是必然的,但可能会导致以下问题。

  • 脏读(Dirty read)。有T1、T2两个事务,T1改写了数据但尚未提交,T2却可以读取到改写后的数据。
  • 不可重复读(Nonrepeatable read)。有T1、T2两个事务。T1多次执行相同的查询,但得到的结果却不相同。通常是因为T2在T1查询期间对数据做了更新。
  • 幻读(Phantom reads)。有T1、T2两个事务。当T1正在读取记录时,T2并发插入了记录,幻读发生了。其实跟不可重复读类似。

理想状态下,所有的事务都应该隔离,从而防止以上情况出现。然而,完全将事务隔离,将大大降低性能。因为隔离要锁定数据行或者数据表,会阻碍并发,要求事务相互等待来完成工作。所以,就区分了几种隔离级别,来灵活应对不同场景下的数据要求。

隔离级别 含义 DEFAULT 这是Spring中的事务隔离级别默认值。它代表使用底层数据库的默认隔离级别。MySQL默认是“可重复读”,Oracle默认是“提交读”。 READ_UNCOMMITTED 未提交读,允许读取到尚未提交的数据。会导致脏读、不可重复读和幻读。 READ_COMMITTED 已提交读,允许读取到已经提交的事务数据。可以防止脏读,但仍会出现不可重复读和幻读。 REPEATABLE READ 可重复读,对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可以避免脏读和不可重复读,但幻读仍会发生。 SERIALIZABLE 串行化,所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰。事实上,基本不会使用到这个级别。 2、事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。Spring中定义7种传播行为。

传播行为 含义 PROPAGATION_MANDATORY 表示该方法必须在一个事务中运行。如果当前没有事务,则抛出异常。 PROPAGATION_NEVER 表示该方法不应当在一个事务中运行。如果一个事务正在运行,则抛出异常。 PROPAGATION_NOT_SUPPORTED 表示该方法不应该在一个事务中运行。如果一个现有事务正在进行中,它将在该方法的运行期间被挂起。 PROPAGATION_SUPPORTS 表示当前方法不需要事务性上下文,但是如果有一个事务已经在运行的话,它也可以在这个事务里运行。 PROPAGATION_REQUIRES_NEW 表示当前方法必须在它自己的事务里运行。一个新的事务将被启动,而且如果有一个现有事务在运行的话,则将在这个方法运行期间被挂起。 PROPAGATION_REQUIRES 表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中,该方法将在那个事务中运行,否则就要开始一个新事务。 PROPAGATION_NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。 3、事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。

4、只读属性

事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。如果一个事务只对后端数据库执行读操作,那么该数据库就可能利用那个事务的只读特性,采取某些优化 措施。通过把一个事务声明为只读,可以给后端数据库一个机会来应用那些它认为合适的优化措施。

5、回滚规则

通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。

三、Spring事务的三大接口

1、 PlatformTransactionManager

PlatformTransactionManager是事务管理的抽象层,Spring根据这个抽象层提供许多不同的具体实现。比如DataSourceTransactionManager、JpaTransactionManager、HibernateTransactionManager等。

public interface PlatformTransactionManager {//返回当前活动的事务或创建一个新的事务。//参数definition描述了事务的属性,比如传播行为,隔离级别,超时等TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException//根据给定事务的状态提交给定事务void commit(TransactionStatus status) throws TransactionException;//执行给定事务的回滚void rollback(TransactionStatus status) throws TransactionException;}

2、 TransactionDefinition

定义了事务属性

public interface TransactionDefinition {//事务的7个传播行为int PROPAGATION_REQUIRED = 0;int PROPAGATION_SUPPORTS = 1;int PROPAGATION_MANDATORY = 2;int PROPAGATION_REQUIRES_NEW = 3;int PROPAGATION_NOT_SUPPORTED = 4;int PROPAGATION_NEVER = 5;int PROPAGATION_NESTED = 6;//事务的5个隔离级别int ISOLATION_DEFAULT = -1;int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;//事务超时时间int TIMEOUT_DEFAULT = -1;//返回传播行为int getPropagationBehavior();//返回隔离级别int getIsolationLevel();//返回超时时间int getTimeout();//是否为只读事务boolean isReadOnly();//返回事务的名称String getName();}

3、 TransactionStatus

代表当前事务的状态,也可以对当前事务进行控制。

public interface TransactionStatus extends SavepointManager, Flushable {//当前事务状态是否是新事务boolean isNewTransaction();//当前事务是否有保存点boolean hasSavepoint();//设置当前事务应该回滚,如果设置这个,则commit不起作用void setRollbackOnly();//当前事务是否应该回滚boolean isRollbackOnly();//用于刷新底层会话中的修改到数据库,一般用于刷新如Hibernate/JPA的会话, //可能对如JDBC类型的事务无任何影响void flush();//当前事务否已经完成boolean isCompleted();}

四、事务的实现方式

Spring中的事务实现方式,总体可以分为两类:编程式事务和声明式事务。

1、编程式事务

顾名思义,编程式事务就是以代码编程的方式来控制事务的运行。我们来看一个事例。 PlatformTransactionManager采用Spring JDBC的事务管理器。数据源是一个数据库连接池,先看下XML配置。

在代码中直接通过事务管理器即可控制事务的运行。我们看一个Service中的方法。

public void insertUser(User user) {TransactionStatus txStatus = transactionManager.getTransaction(transactionDefinition);try {System.out.println("----------新增用户信息------------");transactionManager.commit(txStatus);} catch (Exception e) {System.out.println("保存用户信息发送异常,"+e);transactionManager.rollback(txStatus);}}

通过以上方式就完成了以编程式事务对业务方法的管理。当然了,它的缺点也很明显,事务代码和业务代码糅杂在一起,破坏了业务代码条理性,而且也不利于维护和扩展。 有没有更好的实现方法呢?结合我们上一节学习的Spring AOP知识,应该怎么做呢?

2、声明式事务

没错,利用Spring AOP对方法进行拦截。在方法开始之前创建或者加入一个事务,在方法执行完毕之后根据情况提交或回滚事务。这个就是声明式事务。我们来看一个基于 命名空间的声明式事务管理。

首先,还是先配置一个事务管理器。

其次,通过AOP标签配置一个advisor,它包含一个advice和一个pointcut。

    

最后,通过tx标签定义一个advice。它本身是一个事务的通知,当前要包含事务的管理器和事务的属性。

通过这种方式,我们的业务代码不需要添加任何关于事务的代码,就可以完成事务的操作。在实际开发中,我们大部分也都是使用这种方式或者通过Annotation的方式来配置事务,而不大可能使用编程式事务。

五、源码解析

啰嗦了这么多,是为了先把事务的运行规则、属性讲清楚,不然上来就是源码,容易晕车哈。源码以tx标签为例的配置方式进行分析,编程式事务和Annotation注解方式的事务本章节暂不涉及。

1、tx标签的解析

tx标签的解析,在Spring扫描XML的时候就被加载到了,具体会定位到org.springframework.transaction.config.TxAdviceBeanDefinitionParser类。然后调用类的parse()方法,但是我们发现此类并没有parse方法,往上找最后调用到父类的父类AbstractBeanDefinitionParser.parse()。最后返回一个构建完毕的BeanDefinition对象,并注册到bean容器中,等待下一步的实例化。整个过程我们可以分为三个步骤来看。

  • 1、创建TransactionInterceptor

TransactionInterceptor类就是事务处理的拦截器类,它实现了MethodInterceptor接口。在调用代理类的invoke方法时,实际调用的就是这个类的invoke。

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();//getBeanClass调用到子类的方法,这个子类就是TxAdviceBeanDefinitionParser//它返回的就是return TransactionInterceptor.class;Class> beanClass = getBeanClass(element);if (beanClass != null) {builder.getRawBeanDefinition().setBeanClass(beanClass);}builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));//调用子类TxAdviceBeanDefinitionParser方法doParse(element, parserContext, builder);return builder.getBeanDefinition();}
  • 2、解析事务属性

第一步创建了TransactionInterceptor的BeanDefinition对象,然后调用子类的doParse方法解析子节点进行事务属性的添加。

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {//将配置的事务管理器添加到属性builder.addPropertyReference("transactionManager
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值