本篇接spring事务总篇,之前介绍了找合适的的切面,创建代理。但是并没有详细说切面是哪个?方法拦截器的逻辑是哪个。本篇就开始分析下spring事务的切面。
进入到ProxyTransactionManagementConfiguration。
看里面的方法,似乎有点明白了。

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)// 切面,找到了,设置了方法拦截器,设置切点呢?怎么匹配目标方法呢?别急稍后分析。public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource);advisor.setAdvice(transactionInterceptor);if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)// 这个类应该是标注在方法中@Transactional注解解析出来的对象了。public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)// 拦截器,增强的逻辑就在这里面。当然与事务相关,一定离不开事务管理器public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}}

挑简单的说

 @Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)// 这个类应该是标注在方法中@Transactional注解解析出来的对象了。public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}

先看这段逻辑,将注解的静态信息解析成对象。
看下构造函数,会跟踪到此处,创建了一堆解析注解的解析器。

 public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {this.publicMethodsOnly = publicMethodsOnly;if (jta12Present || ejb3Present) {this.annotationParsers = new LinkedHashSet<>(4);this.annotationParsers.add(new SpringTransactionAnnotationParser());if (jta12Present) {this.annotationParsers.add(new JtaTransactionAnnotationParser());}if (ejb3Present) {this.annotationParsers.add(new Ejb3TransactionAnnotationParser());}}else {this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());}}

我们比较关心这个方法;参数是目标类和方法原信息
org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute

 public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// 如果是Object类中的方法,一定是没有注解的,直接返回if (method.getDeclaringClass() == Object.class) {return null;}// 第一步,先看缓存。Object cacheKey = getCacheKey(method, targetClass);TransactionAttribute cached = this.attributeCache.get(cacheKey);if (cached != null) {// 如果是默认的直接返回null.if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return cached;}}// 如果没有缓存呢?解析出注解信息呗。else {// 解析了TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// 开到了吗?如果是空,缓存中设置的是默认属性对象。if (txAttr == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {// 不是null,就放入缓存。String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {DefaultTransactionAttribute dta = (DefaultTransactionAttribute) txAttr;dta.setDescriptor(methodIdentification);dta.resolveAttributeStrings(this.embeddedValueResolver);}if (logger.isTraceEnabled()) {logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);}this.attributeCache.put(cacheKey, txAttr);}return txAttr;}}

从缓存中取,没有就解析。有一些小的逻辑就不看了,看多了也记不住,要理解主干逻辑,理清之后,如果遇到问题再抠细节。一开始直接扣细节,说实话容易把自己扣死。spring中适配中众多的东西,有好多都是没听说的,只要弄懂自己要用的地方的即可,一定不要说弄懂一切,想做完美,最后几乎一定是没有结尾的。


下面看下解析注解的方法
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute

 protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// 这块涉及一些多态的知识,先不说了,容易乱,感兴趣的可以先debug,看看什么起情况下method和specificMethod是不一样。自己写的类用@T标注的,返回的是一样的。Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// 从方法中找TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// 方法中没有从类中找,可能是标注在类上的。txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}if (specificMethod != method) {// Fallback is to look at the original method.txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}// Last fallback is the class of the original method.txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}return null;}

这块有一点其他的知识,应该是属于多态的知识,暂时不说了,平时也用不到;先看主干逻辑,从方法中找注解信息,方法中没有中类中找注解信息。

这块插一嘴:
如果说业务类实现了接口,注解没有标注在实现类中的方法上,而是标注在了接口的方法上,会找到吗?答案是可以的,只不过不推荐,因为只能用于基于接口的代理,基于类的就不行了。等等哈,这个结论是我从网上找的。虽然是加粗了,如果是用cglib代理真的就找不到吗?我是基于springboot 2.4.3测试的,答案是可以找到。也就是说即使你把注解标记在接口中方法上,使用基于类的代理,同样可以找到。如果幸运的话你看到这篇文章,用其他版本测试一下,看看是不是同样的结果。
稍后我会解释为什么标记在接口方法上也能找到的原因,和为什么用类代理同样可以找到。

在问一句,如果我用设置的是用jdk的代理,但是类没有实现接口,会代理上吗?想一想,答案是可以代理上。但是代理用的是cglib了。代理方式换了。能找到代码在哪块实现的吗?
org.springframework.aop.framework.ProxyProcessorSupport#evaluateProxyInterfaces,进入到这个方法,发现有接口的时候设置接口,没有接口的时候设置的是使用类代理。

 protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());boolean hasReasonableProxyInterface = false;for (Class<?> ifc : targetInterfaces) {if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&ifc.getMethods().length > 0) {hasReasonableProxyInterface = true;break;}}if (hasReasonableProxyInterface) {// Must allow for introductions; can't just set interfaces to the target's interfaces only.for (Class<?> ifc : targetInterfaces) {proxyFactory.addInterface(ifc);}}else {proxyFactory.setProxyTargetClass(true);}}

好了,上面说的太多了,下面开始看看findTransactionAttribute方法做了什么操作。跟踪代理进入到了这个方法,用解析器,取解析每个AnnotatedElement ,这里传入的是AnnotatedElement ,因为解析方法和解析类走的都是这个逻辑。

 protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {for (TransactionAnnotationParser parser : this.annotationParsers) {TransactionAttribute attr = parser.parseTransactionAnnotation(element);if (attr != null) {return attr;}}return null;}

org.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)

 public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false);if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}}

重头戏开始了,AnnotatedElementUtils.findMergedAnnotationAttributes这个方法,就是从给的element中找Transactional注解,需要记住的是这个方法用一个递归的过程,就是会往父类中走同样的逻辑操作。也是为什么标记在接口上,同样能给找到。这里就回答了其中一个问题。其实代理主要就是两部分,找到切点,在切点出做切入逻辑操作。这个切点是和你用哪个代理是无关的,只要切点匹配上就可以代理上。表在哪块都可以被找到注解。找到注解了说明切点匹配,直接代理了,这也就为什么说即使用类代理同样可以代理上(可能高版本的逻辑不通,低版本还没有递归找注解的操作,那是一定不能代理上的)

2021.7.27更新

想一想是如何找到与目标类匹配的切面呢? 有一个canApply方法。
org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)

 public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");if (!pc.getClassFilter().matches(targetClass)) {return false;}MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {// No need to iterate the methods if we're matching any method anyway...return true;}IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}Set<Class<?>> classes = new LinkedHashSet<>();// 这里是重点了,如果不是代理的类型,放入目标类if (!Proxy.isProxyClass(targetClass)) {classes.add(ClassUtils.getUserClass(targetClass));}// 这里放入接口类classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));// 这里遍历目标类和接口类中的方法,查看是否匹配for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {if (introductionAwareMethodMatcher != null ?introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :methodMatcher.matches(method, targetClass)) {return true;}}}return false;}

如果是标记在接口上,同样会进入matches 方法。进行匹配。这里也同样解释了为什么标记在接口上,同样可以找到。

parseTransactionAnnotation方法就不看了,就是创建注解对象了RuleBasedTransactionAttribute。
到此解析@Transactional注解的操作就完成了。


下面看下切面。
会自动出入TransactionAttributeSource ,TransactionInterceptor ;TransactionAttributeSource 已经分析过了,TransactionInterceptor 就是切入逻辑,稍后分析。

 @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {// 创建切面类BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();// 设置注解属性advisor.setTransactionAttributeSource(transactionAttributeSource);// 设置拦截器advisor.setAdvice(transactionInterceptor);if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}

进入这个切面类,有个切点实现类,getTransactionAttributeSource方法是刚刚设置的TransactionAttributeSource 类。在切面匹配的时候,会先去切点,切点中再取出MethodMatcher,进行匹配。这个类自身就是一个MethodMatcher,找matches方法。(这块的逻辑找适合切面的那块,有个canApply方法)

 @Nullableprivate TransactionAttributeSource transactionAttributeSource;private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {@Override@Nullableprotected TransactionAttributeSource getTransactionAttributeSource() {return transactionAttributeSource;}};
 @Overridepublic boolean matches(Method method, Class<?> targetClass) {// 这个方法得到的是刚刚我们设置的TransactionAttributeSource ,之后又调用getTransactionAttributeTransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);}

这块的逻辑分析过了,只要方法中有注解的信息说明就匹配上了。

到此切面中的切点信息分析完成了,下面分析切入逻辑也就是拦截器


 @Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}

进入到TransactionInterceptor,因为是拦截器,所以直接看invoke方法。跟踪到org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

     // 得到TransactionAttributeSource,在创建对象的时候已经将TransactionAttributeSource 设置了TransactionAttributeSource tas = getTransactionAttributeSource();// 同样是解析出注解对象final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);final TransactionManager tm = determineTransactionManager(txAttr);

先看这三行的逻辑,解析出注解对象,之前已经分析了,主要看下如何获得事务管理器的。

 protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {// 如果是null返回的是创建对象是设置的管理器if (txAttr == null || this.beanFactory == null) {return getTransactionManager();}String qualifier = txAttr.getQualifier();// 如果注解中设置了管理器的名称,用bean工厂中找出对应的管理器if (StringUtils.hasText(qualifier)) {return determineQualifiedTransactionManager(this.beanFactory, qualifier);}// 如果设置了事务管理器的名称,从bean工厂中找else if (StringUtils.hasText(this.transactionManagerBeanName)) {return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);}// 到了这步,从bean工厂中找出TransactionManager类型的bean,返回。else {TransactionManager defaultTransactionManager = getTransactionManager();if (defaultTransactionManager == null) {defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);if (defaultTransactionManager == null) {defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);}}return defaultTransactionManager;}}

     PlatformTransactionManager ptm = asPlatformTransactionManager(tm);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// 准备事务TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {// 执行目标方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 如果抛异常回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {// Set rollback-only in case of Vavr failure matching our rollback rules...TransactionStatus status = txInfo.getTransactionStatus();if (status != null && txAttr != null) {retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);}}// 没有异常提交commitTransactionAfterReturning(txInfo);return retVal;}

到这步就很明了了,就是在目标方法前后加入切入逻辑,有异常就回滚,没有异常就提交。
具体是如何创建事务,回滚事务,提交事务的,单独分析。。。

【Spring-tx】ProxyTransactionManagementConfiguration类相关推荐

  1. 聊聊spring tx的EnableTransactionManagement

    序 本文主要研究一下spring tx的EnableTransactionManagement EnableTransactionManagement spring-tx-5.1.6.RELEASE- ...

  2. Spring学习-Spring Tx

    一.概述 Spring Tx 全称为 Spring Transaction Management(Spring 事务管理),是 Spring 为 DB 事务管理提供过的一种便捷的接入方式. 二.导入依 ...

  3. 17、Spring Boot普通类调用bean【从零开始学Spring Boot】

    转载:http://blog.csdn.net/linxingliang/article/details/52013017 我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个 ...

  4. Spring JDBC-NamedParameterJdbcTemplate模板类

    概述 示例 BeanPropertySqlParameterSource 使用示例 MapSqlParameterSource使用示例 NamedParameterJdbcTemplate 支持 in ...

  5. 四 Spring的工厂类,xml的配置

    Spring工厂类的结构图: BeanFactory:老版本的工厂类 BeanFactory:调用getBean的时候,才会生产类的实例 ApplicationFactory:新版本的工厂类 加载配置 ...

  6. spring tx:advice 和 aop:config 配置事务

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010741376/article/details/46584463 spring tx:advic ...

  7. spring MVC请求处理类注解属性详解

    spring MVC请求处理类注解属性详解

  8. Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)...

    当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.la ...

  9. spring管理的类如何调用非spring管理的类

    spring管理的类如何调用非spring管理的类. 就是使用一个spring提供的感知概念,在容器启动的时候,注入上下文即可. 下面是一个工具类. 1 import org.springframew ...

  10. spring boot 启动类

    做项目用到spring boot 感觉spring boot用起来比较流畅.想总结一下,别的不多说,从入口开始. spring boot启动类Application.class 不能直接放在main/ ...

最新文章

  1. Mysql数据库权限问题
  2. VMware Fusion下的虚拟机绑定地址
  3. 2015年十佳IDC评选结果:50强名单揭晓
  4. 《Android开发艺术探索》读书笔记——Cha3.2.2使用动画实现View的滑动
  5. 全民大数据时代已来 阿里数加平台详解
  6. EJB3.0 注释小结
  7. cannot find any entry in order attachment link
  8. java 课后习题 奇数排前
  9. XXX packages are looking for funding run `npm fund` for details
  10. python自定义变量名标识符,【python】3 标识符和关键字
  11. STC8A 进行USB直接ISP下载
  12. 用java实现九九乘法表的打印
  13. 学习PPT好帖子 分享之
  14. 设计模式之面向对象七大基本原则
  15. mybase6.0.4的license key的生成方法
  16. CANOpen协议详解(一):CANfestival源码分析
  17. HTTP与HTTPS及计算机热门快捷键
  18. 智能照明控制系统在城市夜景照明工程中的应用
  19. ElasticSearch 邂逅ES
  20. 字节、字、bit、byte到底咋回事

热门文章

  1. 成功,动机与目标 读后感 -- 你苦于老大不小一事无成 我们成功的原因是什么
  2. 机器学习- 吴恩达Andrew Ng Week4 神经网络Neural Networks知识总结
  3. 极客大学架构师训练营 大数据架构、Spark、Storm、Spark Streaming、Flink、HiBench、Impala 第25课 听课总结
  4. 在UITextView显示HTML,以及NSAttributedString乱码问题解决 swift
  5. 2021-09-06146. LRU 缓存机制 哈希表
  6. Dijkstra最短路径算法
  7. webfigure显示到网页上所踩过的坑--自写servlet
  8. python性能分析工具模块_关于Python Profilers性能分析器
  9. CS231n李飞飞计算机视觉 神经网络训练细节part1上
  10. LocED-Location-aware Energy Disggregation Framework