目录
事务
事务是一个由有限操作集合组成的逻辑单元。事务操作包含两个目的,数据一致以及操作隔离。数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效;事务回滚时,保证能够恢复到事务执行之前的状态。操作隔离则是指多个同时执行的事务之间应该相互独立,互不影响
数据库事务 A C I D
属性
- 原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
- 一致性:事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
- 隔离性:多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 持久性:已被提交的事务对数据库的修改应该永久保存在数据库中
数据库事务隔离级别
根据 SQL92
标准,MySQL
的 InnoDB
引擎提供四种隔离级别
- 读未提交(
Read Uncommited
) - 读已提交(
Read Commited
):oracle
默认的隔离级别 - 可重复读(
Repeatable Read
):mysql
默认的隔离级别,其可避免脏读和不可重复读,但不能避免幻读 - 串行化(
Serializable
)
不同的隔离级别带来不同的问题
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 会 | 会 | 会 |
读已提交 | 不会 | 会 | 会 |
可重复读 | 不会 | 不会 | 会 |
可串行化 | 不会 | 不会 | 不会 |
Spring
中事务传播特性
propagation_requierd
: 如果当前没有事务,就新建一个事务。如果存在事务,就加入到这个事务中。这是spring
默认的propagation_supports
: 支持当前事务。如果当前没有事务,就以非事务方法执行propagation_mandatory
: 使用当前事务,如果没有当前事务,就抛出异常propagation_requierd_new
: 新建事务,如果当前存在事务,把当前事务挂起propagation_not_supported
: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起propagation_never
: 以非事务方式执行操作,如果当前事务存在则抛出异常propagation_nested
: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与
propagation_required
类似的操作
Spring
中事务的隔离级别
隔离级别:若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读
枚举 Isolation
中定义了 5
个表示隔离级别的值
Isolation.DEFAULT
:使用各个数据库默认的隔离级别,是Spring
默认的隔离级别Isolation.READ_UNCOMMITTED
:读取未提交数据(会出现脏读,不可重复读)Isolation.READ_COMMITTED
:读取已提交数据(会出现不可重复读和幻读)Isolation.REPEATABLE_READ
:可重复读(会出现幻读)Isolation.SERIALIZABLE
:串行化
spring aop
原理
动态代理是 spring
实现 aop
的默认方式,分为两种:
JDK
动态代理和CGLIB
动态代理。JDK
动态代理面向接口,通过反射生成目标代理接口的匿名实现类CGLIB
动态代理则通过继承,使用字节码增强技术(或者objenesis
类库)为目标代理类生成代理子类
spring
默认对接口实现使用 JDK
动态代理,对具体类使用 CGLIB
,同时也支持配置全局使用 CGLIB
来生成代理对象
spring aop
方法增强
- 前置增强(
org.springframework.aop.BeforeAdvice
):在目标方法执行之前进行增强 - 后置增强(
org.springframework.aop.AfterReturningAdvice
):在目标方法执行之后进行增强 - 环绕增强(
org.aopalliance.intercept.MethodInterceptor
):在目标方法执行前后都执行增强 - 异常抛出增强(
org.springframework.aop.ThrowsAdvice
):在目标方法抛出异常后执行增强 - 引介增强(
org.springframework.aop.IntroductionInterceptor
):为目标类添加新的方法和属性
声明式事务的实现就是通过 环绕增强
的方式,在 目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务
,事务拦截器的继承关系图可以体现这一点
spring
事务相关源码
统一一致的事务抽象是 spring
框架的一大优势,无论是全局事务还是本地事务,JTA、JDBC、Hibernate
还是 JPA,spring
都使用统一的编程模型,使得应用程序可以很容易地在全局事务与本地事务,或者不同的事务框架之间进行切换。下图是 spring
事务抽象的核心类图:
接口 PlatformTransactionManager
定义了事务操作的行为,其依赖 TransactionDefinition
和 TransactionStatus
接口,其实大部分的事务属性和行为我们以 MySQL
数据库为例已经有过了解,这里再对应介绍下
PlatformTransactionManager
接口:事务管理器getTransaction
方法:事务获取操作,根据事务属性定义,获取当前事务或者创建新事物commit
方法:事务提交操作,注意这里所说的提交并非直接提交事务,而是根据当前事务状态执行提交或者回滚操作rollback
方法:事务回滚操作,同样,也并非一定直接回滚事务,也有可能只是标记事务为只读,等待其他调用方执行回滚TransactionDefinition
接口:事务属性定义getPropagationBehavior
方法:返回事务的传播属性,默认是propagation_requierd
getIsolationLevel
方法:返回事务隔离级别,事务隔离级别只有在创建新事务时才有效,也就是说只对应传播属性propagation_requierd
和propagation_requierd_new
getTimeout
方法:返回事务超时时间,以秒为单位,同样只有在创建新事务时才有效isReadOnly
方法:是否优化为只读事务,支持这项属性的事务管理器会将事务标记为只读,只读事务不允许有写操作,不支持只读属性的事务管理器需要忽略这项设置,这一点跟其他事务属性定义不同,针对其他不支持的属性设置,事务管理器应该抛出异常getName
方法:返回事务名称,声明式事务中默认值为“类的完全限定名.方法名”TransactionStatus
:当前事务状态isNewTransaction
方法:当前方法是否创建了新事务(区别于使用现有事务以及没有事务)hasSavepoint
方法:在嵌套事务场景中,判断当前事务是否包含保存点setRollbackOnly
和isRollbackOnly
方法:只读属性设置(主要用于标记事务,等待回滚)和查询flush
方法:刷新底层会话中的修改到数据库,一般用于刷新如Hibernate/JPA
的会话,是否生效由具体事务资源实现决定isCompleted
方法:判断当前事务是否已完成(已提交或者已回滚)
部分 spring
包含的对 PlatformTransactionManager
的实现类如下图所示:
AbstractPlatformTransactionManager
抽象类实现了 spring
事务的标准流程,其子类 DataSourceTransactionManager
是我们使用较多的 JDBC
单数据源事务管理器,而 JtaTransactionManager
是 JTA(Java Transaction API)
规范的实现类,另外两个则分别是 JavaEE
容器 WebLogic
和 WebSphere
的 JTA
事务管理器的具体实现
spring
事务切面
之前提到,spring
采用 aop
来实现声明式事务,那么事务的 aop
切面是如何织入的呢?这一点涉及到 aop
动态代理对象的生成过程
代理对象生成的核心类是 AbstractAutoProxyCreator
,实现了 beanPostProcessor
接口,会在 bean
初始化完成之后,通过 postProcessAfterInitialization
方法生成代理对象**,关于 beanPostProcessor
在 bean
生命周期中的作用,可参考一些常用的spring 的扩展接口
看一下 AbstractAutoProxyCreator
类的核心代码,主要关注三个方法:postProcessAfterInitialization、wrapIfNecessary
和 createProxy
,为了突出核心流程,以注释代替了部分代码的具体实现,后续的源码分析也采用相同的处理
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 创建代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 参数检查,跳过已经执行过代理对象生成,或者已知的不需要生成代理对象的Bean
...
// Create proxy if we have advice.
// 查询当前Bean所有的AOP增强配置,最终是通过AOPUtils工具类实现
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 执行AOP织入,创建代理对象
Object proxy = createProxy(
bean.