美文网首页
【每天学点Spring】TransactionIntercept

【每天学点Spring】TransactionIntercept

作者: 伊丽莎白2015 | 来源:发表于2022-10-03 19:27 被阅读0次

本文基于:Spring Boot 2.7.0

  • 本文介绍了在Spring Boot框架下,我们通常会在Service类中的某个方法使用@Transactional注解来开启事务,Spring Boot在启动的时候是如何set up事务相关的配置。
  • 以及在Spring Data JPA中的默认事务配置。

1. TransactionInterceptor为什么重要?

【每天学点Spring】Spring AOP APIs学习,以及在TransactionInterceptor中的应用中介绍了TransactionInterceptor的重要方法,即invoke(invocation)

TransactionInterceptor

下图可知TransactionInterceptor继承了抽象类:TransactionAspectSupport

image.png

每个被标注@Transactional注解的方法,都会经过TransactionInterceptorinvoke方法,在该方法中,发现调用了位于上层抽象类TransactionAspectSupportinvokeWithinTransaction

image.png

在抽象类TransactionAspectSupport中的invokeWithinTransaction中,可以看到:

  • 【Step-1】拿到当前的TransactionManager:final TransactionManager tm = determineTransactionManager(txAttr);
  • 【Step-2】是否需要重新创建Transaction:TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
  • 【Step-3】目标方法的执行:retVal = invocation.proceedWithInvocation();
  • 【Step-4】如果遇到错误,是否需要回滚:completeTransactionAfterThrowing(txInfo, ex);
  • 【Step-5】执行完目标方法后,clean transaction相关的信息:cleanupTransactionInfo(txInfo);
  • 【Step-6】返回前提交Transaction:commitTransactionAfterReturning(txInfo);

下图是invoke调用链:
最右的...一般是transactionManager,因为本文主要讨论TransactionInterceptor,所以略。

TransactionInterceptor调用过程

回答本章节的问题:
TransactionInterceptor为何重要?原因是它实现了MethodInterceptor接口,本质上它的invoke方法即切面中的@Around方法,通过切面实现了Transaction的创建(if needed),回滚(如果报错),提交(如果没有报错)。

它本身虽然不做Transaction的逻辑,但它是@Transactional的入口。

2. TransactionInterceptor类如何在Spring Boot的项目中被创建的

因为我们都知道Spring的项目只要标记注解@SpringBootApplication,引入相应的Transaction的包,就能直接使用@Transactional的声明式注解了,那么是如何被创建的呢?

2.1 给TransactionInterceptor构造器打断点

TransactionInterceptor构造器中打个断点,启动程序后,进入了断点,在调用链上看到上述的TransactionInterceptor是通过类ProxyTransactionManagementConfiguration创建的:

image.png
2.2 找到ProxyTransactionManagementConfiguration

ProxyTransactionManagementConfiguration的类的注释上看到TransactionManagementConfigurationSelector

image.png
2.3 通过TransactionManagementConfigurationSelector的selectImports创建了上述#2.2的类

TransactionManagementConfigurationSelector类的方法selectImports(adviceMode)中可以看到返回了ProxyTransactionManagementConfiguration.class.getName()。
这个类是ImportSelector的实现类,所以方法selectImports返回的类名,最终会被实例化成Spring Bean。

我们在selectImports(adviceMode)打断点,运行,进入断点方法: image.png
2.4 通过给selectImports方法打断点找到TransactionAutoConfiguration

查看调用链,可以看到最终是TransactionAutoConfigurationCglibAutoProxyConfiguration类调用的上述的import类。
TransactionAutoConfiguration位于spring-boot-autoconfigure-2.7.0.jar包中:

image.png

在文章【Spring Boot相关】@SpringBootApplication注解中有介绍到,一般的AutoConfiguration,可能会在spring-boot-autoconfigure-{version}.jar中的META-INF/spring-autoconfigure-metadata.properties,如下:

image.png
2.5 那么TransactionAutoConfiguration中的CglibAutoProxyConfiguration是怎么引用到#2.3的TransactionManagementConfigurationSelector

CglibAutoProxyConfiguration的截图中,可以看到类上的注解@EnableTransactionManagement

image.png

@EnableTransactionManagement的定义中可以看到通过Import注解引用了#2.3的TransactionManagementConfigurationSelector

image.png

【小结下】
Spring Boot项目通过AutoConfiguration的方式,引入了【TransactionAutoConfiguration】,再通过cglib的代理类上的注解@EnableTransactionManagement,最终创建了本文的类:【TransactionInterceptor】。

@EnableTransactionManagement位于spring-tx-5.3.20.jar包中。
它的官方在线文档:https://docs.spring.io/spring-framework/docs/5.3.20/javadoc-api/org/springframework/transaction/annotation/EnableTransactionManagement.html

@EnableTransactionManagement激活了Spring的声明式事务的能力。类似于传统的xml中的<tx:annotation-driven/>

3. TransactionInterceptor的PointCut设定

通过#2的EnableTransactionManagement,只是创建了TransactionInterceptor类,还需要一个Pointcut来对这个切面方法进行绑定(即需要告知切面一个入口)。

关于更多的Pointcut绑定,参考之前的文章:【每天学点Spring】Spring AOP APIs学习,以及在TransactionInterceptor中的应用 第#1.3的示例(如同AOP中的光定义@Around本身不够,还需要定义@Pointcut)。

Spring Boot是如何在启动的时候完成TransactionInterceptor与Pointcut绑定的呢?

思路:我们可以从@Transactional注解出发,看看Spring是怎么解析这个注解的,那么也就知道了如何对TransactionInterceptor进行绑定的了,在文件框索框中scope选择All Places:

image.png

SpringTransactionAnnotationParser类的parseTransactionAnnotation打断点,可以看到:
在Bean的initialize阶段,通过AnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInitialization方法中,就有判断,是否需要wrap(抽象类AbstractAutoProxyCreatorwrapIfNecessary方法):

image.png

AbstractAutoProxyCreator中:如果需要wrap,则放入一个Map中,值为True,如果不需要wrap,则值为False:

image.png

AbstractAutoProxyCreator位于:spring-aop-5.3.20.jar中。

那么如果判断需不需要wrap?
这个则是在spring-tx-5.3.20.jar中判断的,在类TransactionAttributeSourcePointcut中有个matches方法:

image.png

最终的最终,就会调用到我们全局搜索@Transactional注解的类SpringTransactionAnnotationParser中,如果parse发现有该注解,则返回一个TransactionAttribute,然后经过一系列的逻辑,判定需要warp,最终交于上述的spring-aop包进行wrap(即增强类):

image.png

测试:
我们有两个service bean:

  • courseService,方法上有@Transactional
  • studentService,方法上没有@Transactional

AbstractAutoProxyCreatorwrapIfNecessary方法中可以看到,标注了@Transactional的类是需要wrap的:

image.png
image.png 那么wrap后的类是长啥样的呢?可以看出是通过cglib动态代理的(另一种是jdk的动态代理,Spring Boot高版本的默认的是cglib的动态代理): image.png 而StudentServiceImpl就是一个普通的bean: image.png

【总结】

  • 分析了Spring Boot在启动的时候会读取定义的autoconfig类:TransactionAutoConfiguration,通过这个类的EnableTransactionManagement实现TransactionInterceptor的创建。
  • 并在Bean create的时候,进行拦截,判断是否有@Transactional注解,如果有,则生成动态代理类。
  • 这样,在运行的时候,动态代理类就会进入TransactionInterceptor的invoke方法,实现transaction的操作(如提交,回滚等)。

4. Spring Data JPA相关的事务

如果在上述的CourseServiceImpl中引入Spring Jpa的CourseRepository:

@Service("courseService")
public class CourseServiceImpl implements CourseService {
    @Autowired
    private CourseRepository courseRepository;

    @Transactional
    public List<Course> list() {
        return courseRepository.findAll();
    }
}

为了更好的观察,我们可以开启transaction的debug log,在application.yaml中配置:

logging:
  level:
    org.springframework.transaction.interceptor: trace

日志打印,我们可以看到,进入了两次TransactionInterceptor:

2022-10-01 18:41:34.683 TRACE 1288 --- [nio-8080-exec-1] o.s.t.i.TransactionInterceptor : Getting transaction for [com.service.impl.CourseServiceImpl.list]
2022-10-01 18:41:34.695 TRACE 1288 --- [nio-8080-exec-1] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
2022-10-01 18:41:34.856 TRACE 1288 --- [nio-8080-exec-1] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
2022-10-01 18:41:34.857 TRACE 1288 --- [nio-8080-exec-1] o.s.t.i.TransactionInterceptor : Completing transaction for [com.service.impl.CourseServiceImpl.list]

可以看到除了CourseServiceImpl外,JpaRepository也会有transaction。

我们可以打个断点在commitTransactionAfterReturning方法上,可以看到先是JpaRepository的transaction先commit,然后再是调用它的CourseServiceImpl,TransactionInterceptor不是同一个Instance,但它里面重要的成员:transactionManager以及它的DataSource都是同一个。

image.png image.png

另外,可以看到findAll()方法的Transactional是readOnly的。
即:默认情况下,repository 接口中的CRUD方法都是被@Transactional注解修饰了的。读的操作方法,@Transactional注解的readOnly属性是被设置为true的,就是只读状态;CRUD中的其他方法被@Transactional修饰,就是非只读。

关于Spring Data JPA set up,另写一篇文章。
Spring Data JPA文档:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

相关文章

网友评论

      本文标题:【每天学点Spring】TransactionIntercept

      本文链接:https://www.haomeiwen.com/subject/ujyvortx.html