声明式事务
spring事务,是通过数据库连接来实现的,当前线程中保存了一个map,key是数据原,value是数据库连接
我们说的同一个事务,其实指的是同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。
如果在不同的线程,拿到的数据库连接肯定是不一样的,那么肯定也就是不同的事务,所以不要在事务中开启另外的线程去处理业务逻辑,这样会导致业务失效
将MainConfig注册成为一个配置类,效果等价于给它加上@Configuration注解
不激活aop的注解,也能使用声明式事务,但是就无法暴露动态代理对象,也就无法通过AopContext的形式,显示拿到当前类的动态代理对象了
第一个是事务转有的后置处理器,第三个是aop转有的后置处理器。
如果同时激活了事务和aop的注解,那么ioc容器中不会同时有上面两个后置处理器,aop的后置处理器会覆盖掉事务的后置处理器。
事务和普通aop的不同点是
1. 普通aop的那些增强逻辑,都是用户通过@Before,@After等注解来自定义的。这些自定义的增强逻辑,需要spring通过后置处理器如解析它们,把它们解析成一个个得Advisor。
事务的增强逻辑是内置的,也就是spring自己写的。所以Spring就自己给我们写好了一个事务专有的Advisor,并注册到了ioc容器中。
2. 普通aop的匹配,是通过AspectJ的切点表达式来进行匹配,而事务看某个方法或者类是否命中了事务专有的Advisor,是通过解析@Transactional注解
这里,spring通过配置类,给我们自动注入了一个解析@Transactional注解的bean,
spring能自己提供,也就意味着,我们也能自己自定义一个这样的bean,并通过我们自己的配置类,将其注入到ioc容器中去
这里,就是Spring自己给我们写好了一个事务专有的Advisor,并注册到了ioc容器中
看第49行,也设置进去了一个通知(也就是,关于事务方面的增强逻辑)
通过这种方式,来自动的往ioc容器中注册了一个事务专有的Advisor
小结:
1. aop是因为要解析切点表达式,来决定给那些bean生成动态代理对象。而事务就没那么复杂,只要看哪个类或者哪个类的方法上有@Transactional注解,就直接给它创建动态代理对象就好了。
2. 普通aop的解析切面,比较复杂,要把各种切面的注解解析成一个个Advisor。而事务的解析切面就简单了,Spring自己就提供了一个Advisor,都不需要去解析,直接就能拿到,拿到后事务的第一步解析切面的步骤就完成了。
3. 一共三步:解析切面,创建动态代理,调用代理方法
事务的解析切面逻辑
Bean后置处理器的注册与实例化
540行,先通过扫描配置类,把配置类中用过通过@Bean自定义的Bean后置处理器,与通过Import注册到ioc容器中的第三方组件自己的Bean后置处理器等多种形式的处理器,通过在第543行中,来将它们全部从BeanDefinition,调用getBean方法实例化成处理器,并存入Ioc容器中的专有list中去
存入专有list之前,还会看这些PriorityOrdered之类的排序接口,来决定各个后置处理器存入list的先后顺序。
createBean方法中有三大步:实例化Bean ,populateBean,initializeBean。
上面就是在执行 initializeBean方法,执行初始化之前,会挨个调用每个后置处理器的前置方法,初始化后,会调用后置方法。
事务的启动流程中的一些初始化动作
事务的动态代理对象生成流程
事务的动态代理方法的调用
1. 无论是普通aop还是事务,如果是走的jdk动态代理,那么就都会执行上面类的invoke方法(因为它,实现了InvocationHandler接口)
2. 就是每次在执行上面的invoke方法时,就会把当前动态代理对象通过AopContext存入线程对象中,从而
这里才能在同一个方法内部(同一个线程),取到当前类的动态代理对象
这个就是事务专有Advisor的Advice,这个Advice用事务将目标方法包裹起来。
开启事务
激活事务
doBegin方法做的事情,就是开始事务
无论是普通aop还是事务,都是通过上面的逻辑走的责任链。
上面这里,也就是责任链走完后,开始执行Jointpoint也就是执行真正的目标方法。
事务的回滚
判断是否已存在外层事务的方式
内层调用suspend去挂起外部事务时,首先会将ConnectionHolder置空,然后内层调用doBegin创建新的内层事务,此时就会拿到一个新的connection,内层把这个新的connection设置到ConnectionHolder中去。
这个事务同步管理器的作用,还不是特别了解