SpringBoot(Spring) 事务
2021-12-22 22:05:50 27 举报
AI智能生成
spring事务
作者其他创作
大纲/内容
https://github.com/HackerFight/tx-demo.git
github
https://gitee.com/hacker9/tx-demo.git
gitee
重点关注 UserService
用maven构建的springboot项目
1.案例演示
创建这个对象时,会在其内部创建一个 SpringTransactionAnnotationParser 对象,将会去解析 @Transactional 注解
AnnotationTransactionAttributeSource
1.导入了 TransactionAttributeSource
这个拦截器非常重要
implements MethodInterceptor,Advice
2.导入了 TransactionInterceptor
beanName: org.springframework.transaction.config.internalTransactionAdvisor
implements PointcutAdvisor
就是1中导入的对象
设置 TransactionAttributeSource 属性
就是 2中导入的对象
设置 Advice 属性
3.导入了 BeanFactoryTransactionAttributeSourceAdvisor
1.给容器导入了一个配置类 ProxyTransactionManagementConfiguration
1.直译过来就是 基础增强自动代理创造器
BeanPostProcessor
AbstractAutoProxyCreator
AbstractAdvisorAutoProxyCreator
继承关系图
2.继承关系
它将后置方法是我们分析的入口和重点
3.不难发现,它确实是一个后置处理器,其中 AbstractAutoProxyCreator 非常的重要!!!
2.给容器导入一个 处理器 InfrastructureAdvisorAutoProxyCreator
做了什么?
1.在配置类上添加 @EnableTransactionManagement 注解
2.在业务类上或者业务类的方法中添加 @Transactional 注解
1.使用事务的前提条件
BeanPostProcessor#postProcessAfterInitialization
1.这个处理器也是 SmartInstantiationAwareBeanPostProcessor 类型,将在实例化前进行一次调用,有机会返回一个对象,一旦有对象返回,那么将不会在经过spring对象创建的三大步骤:实例-属性赋值-初始化 过程,所以这个方法可以忽略
这个是循环引用会用到的,可以忽略
earlyProxyReferences
这个很重要,我们知道所有的处理器是针对所有的bean的,所以说这个属性其实会存储所有的bean(不包括上面循环引用的),如果value=true,则就是我们需要增强的bean
advisedBeans
这个属性也可以忽略,主要是获取类型的
proxyTypes
2.三个Map属性分析
参考步骤1,如果创建了对象,则将会保存到这里,这个也可以忽略
targetSourcedBeans
3.一个Set属性
我们要关注的就是:AbstractAutoProxyCreator
1.spring的后置处理器在bean初始化后将会进行处理器方法的调用
1.处理器是针对所有的bean的,所以可以打一个条件断点,当userService初始化后来到这里
这个接口的翻译:保存AOP通知(在连接点上采取的操作)的基本接口和确定通知适用性的过滤器(如切入点)。其中通知就是我们的方法,比如,@Transaction 注解标注的方法就可以理解为一个通知,或者AspectJ 中的前置通知,后置通知等等,这些都是方法,只不过是有一点特殊的方法
1.什么是增强器?(Advisor)
返回的是一个集合,但实际上就一个,就是前面 @EnableTransactionManagement 注解导入的 BeanFactoryTransactionAttributeSourceAdvisor
2.发现候选的增强器(findCandidateAdvisors())
1.因为候选增强器 BeanFactoryTransactionAttributeSourceAdvisor 是一个PointcutAdvisor 类型,所以进行强转
1.用来标识对什么方法进入代理
2.是 TransactionAttributeSourcePointcut 类,直接new的
2.强转后获取切点
3.遍历当前业务类的所有方法,一旦发现有一个方法标注了 @Transactional 注解,则表示发现了可用的增强器
2.通过遍历所有的候选增强器(这里实际上就一个)和我们的当前业务类UserService 去找可用的增强器(font color=\"#1976d2\
3.发现可以用的增强器(font color=\"#1976d2\
2.发现符合条件的增强器(font color=\"#1976d2\
1.将当前业务bean 放到 advisedBeans 属性中,value=font color=\"#4caf50\
DefaultAopProxyFactory
1.通过 AopProxyFactory 去获取
基于JDK的动态代理,需要实现接口
JdkDynamicAopProxy
基于Cglib的,基于修改字节码文件,派生子类
当调用业务类UserService 中的方法时,将会被它拦截,来到 intercept 方法中
用到的拦截器是:DynamicAdvisedInterceptor
我这里没有实现接口,所以使用的是Cglib的动态代理
ObjenesisCglibAopProxy
2.根据 DefaultAopProxyFactory 一个具体的代理对象,这里用到了简单工厂模式
2.获取代理对象
1.创建 ProxyFactory 对象
2.创建代理对象
3.将代理类放入 proxyTypes 属性中
4.返回步骤2中的代理对象
3.一旦有符合条件的增强器
4.如果没有符合条件的增强器,则说明当前业务bean不需要包装,直接返回bean即可
2.AbstractAutoProxyCreator#postProcessAfterInitialization() 方法分析
当调用业务方法,来到代理对象的拦截方法
当前业务bean需要增强,返回一个代理对象
3.上面步骤执行完了,我觉得主要做了一件事
因为AspectJ 也是会来到这里,它可以有前置,后置,返回等等,所以有链;而我这里就是一个普通的事务,所以它返回的就是一个
1.为什么是链(集合)?
2.遍历所有的增强器,实际上这里就一个 BeanFactoryTransactionAttributeSourceAdvisor
这个Advice 就是前面配置类导入的 TransactionInterceptor
3.如果发现了 @Transactional 注解的方法,则将当前增强器转换为 MethodInterceptor 拦截器
1.获取拦截器调用链(集合)
就是调用了UserService 中没有标注 @Transactional 注解的方法,直接调用即可
2.如果拦截器链是空的并且方法时public的,那么说明调用的是cglib代理类中一个普通方法
这个类是对事务操作一个非常重要的类
1.获取事务管理器 PlatformTransactionManager
最终是映射到 java.sql.Connection 中
这里将会把注解的全部属性映射到 TransactionManager 中
继续调用 tm.getTransaction(txAttr)
在 createTransactionIfNecessary() 方法中,可以看他上面的注释
2.设置自动提交为 false
事务和AspectJ 本质上一样的,只不过事务显得普通一点而已
这里请看注释:这是一个around建议:调用链中的下一个拦截器。这通常会导致调用目标对象。
3.开始调目标用方法
4.如果成功,则commit
5.如果失败,则rollback
3.如果拦截器链不为空,则创建一个 CglibMethodInvocation 对象,并调用 proceed() 方法
4.当调用业务方法
2.原理探究
1.Cglib & JDK
https://github.com/HackerFight/proxy.git
2.github
https://gitee.com/hacker9/proxy.git
3.gitee
3.动态代理演示
这个是最简单的,也是最常用,只需要使用 @Transaciton 和 @EnableTransactionManagement 注解即可
1.声明式事务
TransactionTemplate
可以通过事务模板
TransactionManager
可以通过事务管理器
编程式事务演示
2.编程式事务
4.使用声明式事务与编程式事务
采用数据库的隔离级别:(MySQL: 可重复读、Oracle: 读已提交)
最好就使用这个参数,默认也是这个参数
1.@Transactional(isolation = Isolation.DEFAULT)
2.@Transactional(isolation = Isolation.READ_UNCOMMITTED)
3.@Transactional(isolation = Isolation.READ_COMMITTED)
4.@Transactional(isolation = Isolation.REPEATABLE_READ)
5.@Transactional(isolation = Isolation.SERIALIZABLE)
隔离级别
1.Isolation 指定事务的隔离级别
当你能确保整个事务过程中只对数据库执行Select操作的时候,如果将此属性设置为true,则会自动进行优化,提高性能。
2.readOnly 设置是否是只读事务(常用,用来标识是否是只读操作)
this.deadline = new Date(System.currentTimeMillis() + millis)
在这里会将超时时间转换成毫秒,然后加上当前时间,转换为Date类型,赋给截止时间
AbstractPlatformTransactionManager#getTransaction#startTransaction#doBegin
0.开启事务后,会通过 TransactionAspectSupport#invokeWithinTransaction#createTransactionIfNecessary#getTransaction
1.当使用 JdbcTemplate 执行sql语句时,会将超时时间设置到 PreparedStatement 中的 queryTimeout
这就说明,在执行sql之前,以及sql执行中,这两部分花费的时间都是计算到 timeout 中
事务超时时间
3.超时生效与不生效的具体演示
4.默认值是-1,取数据库的事务超时时间
3.timeout 设置事务的超时时间(单位:秒)
1.导致事务回滚的异常类数组
(ex instanceof RuntimeException || ex instanceof Error)
DefaultTransactionAttribute#rollbackOn
2.默认是empty的,此时只有抛出 RuntimeException 或者 Error 类型的异常才会回滚
1.如果rollbackFor 异常和实际发生的异常一样,则肯定回滚
3.实际上这里的 NullPointerException 异常是没用的,最终还是因为实际发生的异常是RuntimeException 而进行回滚
他会根据实际发生的异常去向上匹配
4.rollbackFor 异常回滚
不会导致事务回滚的异常类数组
RuleBasedTransactionAttribute#rollbackOn
和上面相反,这块的关于回滚和不回滚的设计可以学习一下
5.norollbackFor 异常不回滚
1.事务传播行为是Spring框架提供的一种事务管理方式,它不是数据库提供的
2.事务的传播行为主要是应对 事务方法嵌套事务方法
这个是默认选项
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中(共用了同一个事务)。这是最常见的选择。
PROPAGATION_REQUIRED
新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_REQUIRES_NEW
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
PROPAGATION_NESTED
假设当前在事务中。即以事务的形式执行。假设当前不在一个事务中,那么就以非事务的形式执行
PROPAGATION_SUPPORTS
必须在一个事务中执行。也就是说,他仅仅能被一个父事务调用。否则,他就要抛出异常
PROPAGATION_MANDATORY
当前不支持事务。比方ServiceA.methodA的事务级别是PROPAGATION_REQUIRED 。而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到methodA 的ServiceB.methodB时。ServiceA.methodA的事务挂起。而他以非事务的状态执行完,再继续ServiceA.methodA的事务
PROPAGATION_NOT_SUPPORTED
不能在事务中执行。就是不能被事务方法调用,否则抛出异常
PROPAGATION_NEVER
3.事务传播行为的类型
开启新的事务意味着开启了新的连接
1.在不存在事务的情况下:PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED 都是新开启一个事务
2.在已经存在事务的情况下,只有 PROPAGATION_REQUIRES_NEW 会开启新的事务
3.在外围方法开启事务的情况下,Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务
4.在外围方法未开启事务的情况下Propagation.NESTED和Propagation.REQUIRES_NEW 作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。
5.在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。
6.在外围方法开启事务的情况下,Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
4.小总结
子主题
事务传播行为
5.参考测试用例
参考文档
6.参考文档
6.propagation 事务的传播行为
5.事务属性探究
1.方法不是public
2.方法发生了自身调用(异步也是同样的道理)
3.数据库引擎不支持事务-MyISAM
6.事务失效的场景
7.面试题整理
SpringBoot 事务【2.3.12.RELEASE】
0 条评论
下一页