最近在工作中用到了spring的事务管理功能,到项目代码中一看,有声明式的,有编程式的,比较混淆,所以对spring的事务管理做了一个简单的分析,主要回答自己一下几个问题:
1. 声明式事务怎么处理事务?
2. 编程式事务怎么处理事务?
3. 他们之间有什么关系?
4. spring事务管理和传播特性怎么联系起来的?
5. spring事务管理中的核心类是哪几个,分别用来做什么?
了解清楚工作方式后会对代码更有信心.对各位来说下节很多是很容易理解的概念,但为了能说明清楚最后的执行过程,还是想再介绍一下。
一、spring事务的简单介绍
事务,是为了保证在一个方法或代码块中对数据库多次操作的原子性的描述,即事务内的与数据库的多次操作同时成功或同时失败,避免出现数据不一致的情况。
在spring中,提供了两种使用事务的方式:
1. 声明式事务,通过在spring的配置文件中配置隔离级别,传播特性,并结合spring的AOP功能,对所配置的方法做动态代理来达到某方法中的多次数据库操作同时成功,或者同时失败。
2. 编程式事务,通过在方法内部对于数据库的多次操作采用手工事务包裹的方式,达到事务内的数据库操作同时成功,或者同时失败。
二、隔离级别与传播特性的介绍
这里的概念与数据库有莫大的关系:
隔离级别:即数据库对数据的隔离级别,不同的数据库对数据的隔离级别不同,并且可以手工配置,大致分为以下几种,隔离级别由低到高:
Read Uncommitted: 直译就是"读未提交",意思就是即使一个更新语句没有提交,但是别的事务可以读到这个改变.这是很不安全的. ( 隔离级别最低,并发性能高 )
Read Committed: 直译就是"读提交",意思就是语句提交以后即执行了COMMIT以后别的事务就能读到这个改变. (锁定正在读取的行)
Repeatable Read: 直译就是"可以重复读",这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果是一样的. (锁定所读取的所有行)
Serializable: 直译就是"序列化",意思是说这个事务执行的时候不允许别的事务并发执行. (锁表)
特别说明的:
1. Oralce的默认隔离级别为:Read Committed
2. MySQL中的MySQL InnoDB存储引擎 的默认隔离级别是Repeatable Read
3. 设置隔离级别可以通过SQL 语句: set transaction isolation level xxx 实现
传播特性:
1.PROPAGATION_REQUIRED 如果存在一个事务、则支持当前事务。如果没有事务则开启。
2.PROPAGATION_SUPPORTS 如果存在一个事务、则支持当前事务。如果没有事务则非事务执行。
3.PROPAGATION_MANDATORY 如果已经存在一个事务、则支持当前事务。如果没有活动事务则抛出异常。
4.PROPAGATION_REQUIRES_NEW 总是开启一个新的事务、如果已经存在一个事务、则将这个事务挂起。
5.PROPAGATION_NOT_SUPPORTED 总是非事务执行、并挂起任何存在的事务。
6.PROPAGATION_NEVER 总是非事务执行、如果存在一个活动事务则抛出异常。
7.PROPAGATION_NESTED 果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。
它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。
化成表格后是这样:
事务属性
事务1
事务2
Required
无
事务1
事务2
事务1
RequiredNew
无
事务1
事务2
事务2
Support
无
事务1
无
事务1
Mandatory
无
事务1
抛异常
事务1
NOSupport
无
事务1
无
无
Never
无
事务1
无
抛异常
NESTED无
事务1
Required
事务1,若有回滚,不影响事务1
比较特殊的是最后一个NESTED,注释是这样说的:
而我们项目中使用的是DataSourceTransactionManager他是支持该传播特性的:
三、 声明式事务与编程式事务处理方式的不同
这里涉及的类比较多,自己梳理了一下几个核心类和核心变量的包含关系
接下来进入正题,从源码角度去分析两者的实现方式,总的来说,无论是使用AOP的声明式事务还是使用匿名内部类的编程式事务,都是在具体方法调用前根据传播特性声明一个TransactionStatus,
他是用来记录Transation的一些状态以及Transation本身,不同的是,声明式事务在代理类使用了一个ThreadLocal变量来存放TransationStatus(用TransactionInfo包装),并且还提供了一个备份变量,以供还原。
这么做的好处是:
记录代理类在该方法进入前生成的TransactionStatus的,每次进入一个代理类的时候就新生成一个TransactionStatus放进去,方法调用完返回时再释放,类似一个链条。
这么做的好处是:
记录代理类在该方法进入前生成的TransactionStatus的,每次进入一个代理类的时候就新生成一个TransactionStatus放进去,方法调用完返回时再释放,类似一个链条。
这样,每一个层次的代理类就可以再自己代理范围内对事务做处理,比如,设置回滚,提交(事实上内部事务抛出异常并不会马上回滚,而是设置连接的回滚标志,等到返回链条的最顶端时,才会根据标志决定是否回滚)。
而TransationTemplate里没有用ThreadLocal存放,我的理解是,TransationTemplate并不是单例模式使用的,不需要使用ThreadLocal存放。
那,spring是用什么判定是否需要新建一个事务呢?
从上图中可以看到ConnectionHolder里面有个transactionActive的标志位,而他所在的DataSourceTransactionObject是存放在TransationSynchronizationManger(单例)中的ThreadLoal中的,
简单说就是被当前线程共享的变量中,这样,每次进入一个事务时只需要通过该变量中的标志位和用户配置的传播特性共同决定是否需要新建事务就可以了。
这么做的就解释了 下面传播特性的说明:
”PROPAGATION_REQUIRED 如果存在一个事务、则支持当前事务。如果没有事务则开启“ 这是针对当前线程来说的(我很久才明白这个意思)
对于声明式事务,再说明一下:
AManagerImpl{
methodA{
aDao.insert();
bManagerImpl.methodB();
}
}
这个时候如果 传播特性是PROPAGATION_REQUIRED,在进入methodA之前spring 开启一个事务,当代码进入methodB时,通过ConnectionHolder(包装了connection对象)
里的标志位transactionActive看到已经有一个事务了,根据事务的传播特性,不需要重新new一个事务,所以只是单纯的new了一个TransationStatus,用TransactionInfo包装后,
set在代理类的ThreadLocal对象中。然后继续执行methodB的方法。
里的标志位transactionActive看到已经有一个事务了,根据事务的传播特性,不需要重新new一个事务,所以只是单纯的new了一个TransationStatus,用TransactionInfo包装后,
set在代理类的ThreadLocal对象中。然后继续执行methodB的方法。
下面在通过两张截图,仅从调用上看看两种方式的区别:
声明式事务,spring 用CGLIB 做AOP编程,将我们配置的传播特性等属性封装成对象后,进入最主要的代理类TransactionInterceptor的invoke方法:
在看编程式事务,比较简单,看TransactionTemplate类中的方法:
那他们是在哪一步走入同一个流程呢?这一步困扰我很久,那获取事务举例,其实很简单,画个图说明:
虽然层数比较多,但是还是能很清晰的看到,其实两种方式都是最终走到了AbstractPlatformTransactionManager类中,调用方法getTransaction得到transationStatus,
内部是新创建一个Transation还是使用当前Transation,由TransationSynchronizationManger通过ThreadLocal变量中的标志位和用户配置的传播特性共同决定,
其实,第三节第一张图可以看出,spring事务管理的大部分核心操作都在AbstractPlatformTransactionManager类中进行。
四、收获
在这次分析中,感受最深的是spring对于内部类的使用,DataSourceTransactionObject是DataSourceTransactionManager的内部类,TransactionInfo是TransactionAspectSupport
的内部类,这些类都是为外部类单独使用,用于存放当前线程的状态的。这么做的好处是,不必要单独建一个JAVA文件,并且内部类有一定的保护数据的功能。
其次是抽象类和接口的继承,可以看到,AbstractPlatformTransactionManager下通过多态规定了绝大部分的操作流程,而具体实现根据各个子类所针对的不同情况具体实现。
即,父类,规定流程,子类提供实现,这么做的效果非常好,流程清晰,易扩展,实现多种多样。
附上一张AbstractPlatformTransactionManager的继承关系图:
相关推荐
本文将全面分析Spring中的编程式事务管理和声明式事务管理,旨在帮助开发者深入理解这两种事务管理方式,并在实际项目中合理选择。 **编程式事务管理** 编程式事务管理是通过代码直接控制事务的开始、提交、回滚等...
Spring 中的事务处理可以分为两种方式:声明式事务处理和编程式事务处理。声明式事务处理通过 AOP 的实现,把事务管理代码作为方面封装到业务代码中,使得事务管理代码和业务代码解藕。这使得事务管理变得更加灵活...
Spring声明式事务是Java开发中不可或缺的一部分,它利用Spring的AOP(面向切面编程)和代理机制,为开发者提供了一种简洁的方式来管理事务。在本文中,我们将深入探讨Spring声明式事务的工作原理,源码分析,以及...
Spring还支持编程式事务管理,允许通过PlatformTransactionManager接口的API直接操作事务。 2. **IoC 容器**:IoC(Inversion of Control)容器是Spring的核心,负责管理和装配应用中的对象。BeanFactory是基础接口...
首先,Spring事务管理有两种主要模式:编程式事务管理和声明式事务管理。编程式事务管理通过调用`PlatformTransactionManager`接口提供的方法进行显式控制,如`beginTransaction()`, `commit()`, 和`rollback()`。...
当我们谈论“Spring编程式事务”时,这涉及到Spring框架中对数据库事务的管理方式之一。 Spring提供了两种事务管理方式:编程式事务管理和声明式事务管理。编程式事务管理是指在代码中显式地控制事务的开始、提交、...
以上只是Spring源码分析的部分内容,实际源码中还包括Spring的其他模块,如Spring Batch(批处理)、Spring Security(安全)、Spring Integration(集成)等。理解并掌握Spring源码,有助于我们更好地利用Spring...
在事务管理方面,Spring提供了声明式事务管理,使得开发者无需手动控制事务的开始、提交、回滚,只需在配置中声明事务边界,大大简化了事务处理。此外,Spring还支持编程式事务管理,为需要精细控制事务的场景提供...
在事务管理方面,Spring提供了编程式和声明式两种事务管理方式。通过源码,我们可以看到TransactionProxyFactoryBean如何实现事务的声明式管理,以及PlatformTransactionManager接口和其实现类如...
- 支持编程式和声明式事务管理,简化了事务的处理流程。 #### 四、Web层 1. **Web模块**: - 提供了基础的Web集成特性,如多文件上传、使用servlet监听器初始化IOC容器等。 2. **Web-Servlet模块**: - 包含...
在事务管理方面,Spring提供了声明式事务管理,使得开发者无需显式调用开始和结束事务的方法,而是通过配置来控制事务的边界。文档会深入解析TransactionProxyFactoryBean和PlatformTransactionManager的工作机制。 ...
声明式事务管理通过AOP实现,无需编写额外的事务管理代码,而编程式事务管理则允许开发者手动控制事务的边界。 8. **Spring Boot** Spring Boot简化了Spring应用的初始搭建和配置工作,它基于约定优于配置的理念,...
AOP技术支持声明式事务管理、安全和日志记录等面向切面的编程技术。 ### 设计模式 在Spring框架中,使用了多种设计模式来实现框架的灵活性和易用性: - **工厂模式**:Spring的BeanFactory就是一个典型的工厂模式...
《Spring源码分析》 Spring框架作为Java领域中不可或缺的一部分,其强大之处在于它提供了丰富的功能,包括依赖注入(Dependency Injection,简称DI)、面向切面编程(Aspect-Oriented Programming,简称AOP)、事务...
Spring提供了编程式和声明式事务管理两种方式。在`org.springframework.transaction`包中,`PlatformTransactionManager`接口定义了事务管理的基本操作,而`TransactionDefinition`和`TransactionStatus`则分别代表...
Spring支持编程式和声明式事务管理,其中,PlatformTransactionManager接口定义了事务管理的基本操作,而TransactionDefinition和TransactionStatus接口则用于描述事务属性和状态。声明式事务管理基于AOP,通过@...
Spring的事务管理机制包括编程式事务管理和声明式事务管理,这两种方式都基于`spring-tx`模块。在这个源码中,我们可以深入理解Spring如何处理事务的生命周期、回滚规则以及与各种数据源的集成。 首先,`spring-tx`...
`spring-tx`模块则专注于事务管理,支持编程式和声明式事务处理,可以应用于多种事务管理协议,如JTA(Java Transaction API)和JDBC。 `spring-jms`模块提供了与Java消息服务(JMS)的集成,支持消息生产者、消费...
编程式事务管理是通过直接使用Spring提供的事务管理API来管理事务,而声明式事务管理则通过注解或者XML配置的方式来管理事务,更加简洁方便。 具体到事务管理的源码分析,Spring通过定义TransactionDefinition接口...
在Java应用中,Spring框架提供了一种方便的方式来管理事务,即编程式事务管理和声明式事务管理。 1. **编程式事务管理**:开发者需要手动调用`TransactionTemplate`或`PlatformTransactionManager`的相关方法来开始...