目录
声明式事务
事务传播行为
源码解析
开启事务
调用顺序
@EnableTransactionManagement注解的两个作用
引入AutoProxyRegistrar后置处理器
引入ProxyTransactionManagerConfiguration配置类
加载切面
事务的Advisor的注册
事务Advice
事务PointCut
创建动态代理
调用代理方法
声明式事务
- Spring事务,是通过数据库连接来实现的,当前线程中保存了一个map:key是数据源、value是数据库连接
- 我们说的同一个事务,其实指的是同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚
- 如果在不同的线程,拿到的数据库连接肯定是不一样的,那么肯定也就是不同的事务,所以不要在事务中开启另外的线程去处理业务逻辑,这样会导致业务失效
Spring的事务和Spring AOP原理是差不多的,唯一的不同是,Spring AOP的增强逻辑需要我们程序员自己手动的定义各种Advice,但是spring事务的增强逻辑是spring自己内置提前写好的
事务传播行为
此时,第28行会更新失败,而第30行因为在父事务的内部又创建了一个子事务,所以第30行会更新成功
- 一般情况下,第36行的方法需要另起一个类,否则spring不会帮我们重新创建一个新事务(也就是不会重新帮我们执行增强逻辑,在spring事务场景下,执行增强逻辑就是创建新事务)
- 但是,在当前情况下,为什么没有另起一个类依然创建好了一个新的事务,这是因为第30行,AopContext.currentProxy()拿到了暴露在当前线程中的已经包含了增强逻辑的动态代理对象
这是因为JDK动态代理,默认情况下,调用本类的另外的方法,不会重复的执行增强逻辑,不会重复的执行增强逻辑意味着不会重复的创建新的事务
想使用AopContext.currentProxy(),这里就必须开启红框中的配置
不激活aop的注解,也能使用声明式事务,但是就无法暴露动态代理对象,也就无法通过AopContext的形式,显示拿到当前类的动态代理对象了
源码解析
开启事务
将MainConfig注册进AnnotatedConfigApplicationContext成为一个配置类,效果等价于给它加上@Configuration注解
AbstractApplicationContext#refresh()中invokeBeanFactoryPostProcessor()中开始解析配置类的时候,就会去解析@Import注解,因为invokeBeanFactoryPostProcessor()内部会调用ConfigurationClassPostProcessor,ConfigurationClassPostProcessor回去解析配置类,而@Import注解就在配置类头上
默认就是PROXY,动态代理的方式
调用顺序
AbstractApplicationContext#refresh() ->invokeBeanFactoryPostProcessor() ->ConfigurationClassPostProcessor
ConfigurationClassPostProcessor解析配置类时,会去解析@Import注解,解析实现了ImportBeanDefinitionRegistrar接口的类,将实现了ImportBeanDefinitionRegistrar接口的类放入Map<ImportBeanDefinitionRegistrar,AnnotationMetadata>中
AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以AutoProxyRegistrar也会在Map<ImportBeanDefinitionRegistrar,AnnotationMetadata>中,所以这里,也会调起AutoProxyRegistrar#registerBeanDefinitions()中,也就是会调起下面的方法
AutoProxyRegistrar#registerBeanDefinitions()中,就会通过传进来的BeanDefinitionRegistry来向ioc容器中注册进一些BeanPostProcessor从而实现一些spring事务相关的功能
@EnableTransactionManagement注解的两个作用
引入AutoProxyRegistrar后置处理器
- 解析切面
- 创建动态代理
引入ProxyTransactionManagerConfiguration配置类
往IOC容器中事务相关的功能Bean,比如引入事务相关的增强Advisor
AutoProxyRegistrar#registerBeanDefinitions()中,就会通过传进来的BeanDefinitionRegistry来向ioc容器中注册InfrustructureAdvisorAutoProxyCreator这个BeanPostProcessor
注册事务的和注册AOP的后置处理器,使用的是同一个方法,并且在ioc容器中使用的名字都是一样的
比较aop的后置处理器和事务的后置处理器哪个优先级高,aop的后置处理器优先级更高,所以就会调用第129行,把beanClass给覆盖掉(因为事务的后置处理器先被执行进入ioc)
事务和AOP注解同时开启时,AOP的后置处理器,会覆盖掉事务的后置处理器
优先级,aop的后置处理器更高
后置处理器注册好后
就会在bean被实例化、属性注入、初始化后,来到
加载切面
AOP时,就是要在这里解析切面类,解析成为一个个的Advisor
事务时,当然也是要将创建事务的增强逻辑,解析成为一个Advisor
事务的Advisor的注册
ProxyTransactionManagerConfiguration这个也就是一个配置类,配置类中会通过@Bean引入很多实现事务功能的相关Bean,当前ProxyTransactionManagerConfiguration就是自动引入了事务的Advisor
- 可以看到使用SpringBoot时,每引入一个小功能都有一个自己专属的Configuration配置类,每个专属配置类中都有自己相关的功能Bean,
- 比如引入Redis时,就有Redis专属的配置类RedisAutoConfiguration
- 比如引入Spring事务时,就有事务专属的ProxyTransactionManagerConfiguration配置类
事务Advice
后续调用下面的动态代理的pay()方法时,就会来上上面的invoke()方法来做一些事务方面的增强工作
事务PointCut
因为事务没有使用像aop的切点表达式来进行匹配,而是使用了@Transactional注解来做的匹配,只要你方法头上有@Transactional注解,那么就给你执行事务Advice中的增强逻辑创建新事务
TransactionAttributeSource,就是去扫描解析类的那些方法头上有@Transactional注解
- 事务的Advisor是spring自己提供了,并自己注册进ioc容器的,
- AOP的Advisor是需要项目启动时,直接解析出来的
事务aop和普通aop的不同点是
1. 普通aop的那些增强逻辑,都是用户通过@Before,@After等注解来自定义的。这些自定义的增强逻辑,需要spring通过后置处理器如解析它们,把它们解析成一个个得Advisor。
事务的增强逻辑是内置的,也就是spring自己写的。所以Spring就自己给我们写好了一个事务专有的Advisor,并注册到了ioc容器中。
2. 普通aop的匹配,是通过AspectJ的切点表达式来进行匹配,而事务看某个方法或者类是否命中了事务专有的Advisor,是通过解析@Transactional注解
1. aop是因为要解析切点表达式,来决定给那些bean生成动态代理对象。而事务就没那么复杂,只要看哪个类或者哪个类的方法上有@Transactional注解,就直接给它创建动态代理对象就好了。
2. 普通aop的解析切面,比较复杂,要把各种切面的注解解析成一个个Advisor。而事务的解析切面就简单了,Spring自己就提供了一个Advisor,都不需要去解析,直接就能拿到,拿到后事务的第一步解析切面的步骤就完成了
创建动态代理
如果使用第17行,那么使用这样的cglib动态代理时,在本类的一个方法中调用另一个方法,另一个方法也能有增强逻辑
而如果没有使用第17行,那么即便是使用了cglib动态代理时,在本类的一个方法中调用另一个方法,另一个方法也不会有增强逻辑
而spring事务的实现代码中,就算我们设置了proxyTargetClass为true,表示强制使用cglib动态代理,但是因为spring事务的实现代码没有使用上面第17行的调用方式,而是直接去调用了责任链,所以在本类的一个方法中调用另一个方法,另一个方法还是没有增强逻辑