spring事务源码

spring事务的源码,要从@EnableTransactionManagement注解拉开序幕

首先,如果我们要使用spring事务,只需要在配置类上添加@EnableTransactionManagement注解,并且在业务方法上添加@Transactional注解即可(以spring项目为例,SpringBoot项目后面博客中再另说)

@EnableTransactionManagement

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {/** proxyTargetClass*  true:*    无论目标方法是否实现了接口,都使用CGLIB代理*  false:*    如果目标方法实现了接口,使用JDK动态代理*    如果目标方法没有实现接口,使用CGLIB代理*/boolean proxyTargetClass() default false;/*** @return** 事务通知模式(切面织入方式):*  默认是代理模式*/AdviceMode mode() default AdviceMode.PROXY;int order() default 2147483647;
}

可以看到,这个注解也挺简单的,就是通过@Import注解,引入了另外一个bean,通过查看TransactionManagementConfigurationSelector的类继承关系,可以发现,这个类实现了ImportSelector注解,所以,会实现selectImports()方法,在该方法中,注入了两个重要的bean

  1. AutoProxyRegistrar
  2. ProxyTransactionManagementConfiguration

AutoProxyRegistrar

AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以,在registerBeanDefinitions方法中,注入一个bean

InfrastructureAdvisorAutoProxyCreator
可以发现,这个类是一个后置处理器,继承了InstantiationAwareBeanPostProcessor所以:这就是@EnableTransactionManagement的第一个作用,注入了一个后置处理器,这个后置处理器就是用来对事务注解进行增强的

ProxyTransactionManagementConfiguration

该类只有一个注解@Configuration,所以该类是一个配置类,在该类中,通过三个@Bean注解,向spring容器中注入了事务执行时要用到的组件

/*** @return* 注入事务增强器* 这里是创建一个advisor,然后设置切点(TransactionInterceptor)和通知(TransactionAttributeSource)* 这里的BeanFactoryTransactionAttributeSourceAdvisor类似于aop中的advisor*/@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();//事务增强器会解析事务注解的参数信息advisor.setTransactionAttributeSource(transactionAttributeSource());advisor.setAdvice(transactionInterceptor());if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}/*** @return* 往spring容器中注入事务解析器(解析事务注解上的各个参数)* 在执行第八个后置处理器的时候,判断是否需要增强的时候,会解析transaction注解** 这里在new AnnotationTransactionAttributeSource()对象的时候,有一个非常关键的点:*  publicMethodsOnly  这里在调用构造函数的时候,默认初始化该值为true;该值的意思是:只允许public方法进行事务代理**    在后面判断是否可以对方法进行增强的时候,会判断该值,以及对应method是否是public,如果是非public修饰的方法,直接return null,不进行代理增强*/@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}/*** @return* 定义事务拦截器,并将事务拦截器注入到spring容器中*/@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor() {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource());if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}

所以:该类是给spring容器中,注入了三个bean,分别是:事务拦截器、事务解析器、事务增强器

判断是否需要代理

我们知道,AOP的动态代理是在第八个后置处理器调用的时候,判断是否需要增强,如果需要增强,就通过JDK或者CGLIB进行代理,注解版的事务,也是利用了AOP实现的

这是在添加事务注解的方法对应的bean在初始化时,调用到第八个后置处理器的时候,判断是否需要进行增强的调用链,由于这里和AOP是一样的调用链,所以中间的这些过程就不做过多解释了,直接看最后匹配是否需要增强的代码

我们可以暂时先认为:methodMatcher.matches(method, targetClass) 如果这里返回的是true,就是需要进行增强,返回false,就继续遍历下一个方法,进行判断

org.springframework.transaction.interceptor.TransactionAttributeSourcePointcut#matches
// 这里是用获取到的tas来判断method是否有添加注解,如果这里返回false,就表示当前method无需增强,返回true,需要增强
public boolean matches(Method method, Class<?> targetClass) {TransactionAttributeSource tas = this.getTransactionAttributeSource();return tas == null || tas.getTransactionAttribute(method, targetClass) != null;
}org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute/*** 这里是在解析method之前,先判断下之前是否已经解析过*/
@Override@Nullablepublic TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}// First, see if we have a cached value./*** 根据method和targetClass生成一个cacheKey* 如果这里已经对方法进行了一次解析,就会把解析之后获取到的TransactionAttribute对象和对应的key存入到一个map集合中* 这样下次再有地方用到这个方法的时候,就无须再次解析,直接从map中获取即可* 如果这里没有获取到,返回的null,就将value设置为null,写入到map中* 如果获取到对应的txAttr,就setDescriptor设置下该属性,然后写入到map集合中*/Object cacheKey = getCacheKey(method, targetClass);TransactionAttribute cached = this.attributeCache.get(cacheKey);if (cached != null) {// Value will either be canonical value indicating there is no transaction attribute,// or an actual transaction attribute.if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return cached;}}else {// We need to work it out./*** 如果是第一次进入到这里,一定会走这个方法* 这里就是判断当前方法是否是public,是否有添加@Transactional注解*/TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// Put it in the cache./*** 如果当前方法没有添加事务注解,或者不满足生成代理对象的要求,就将value设置为null,存入到这个map集合中*/if (txAttr == null) {this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {// 这里是满足增强的条件,将txAttr放到map集合中,并返回String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);}if (logger.isDebugEnabled()) {logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);}this.attributeCache.put(cacheKey, txAttr);}return txAttr;}}

下面这个方法主要是判断当前method是否是public修饰的,然后在方法中调用findTransactionAttribute来判断方法或者class是否有添加@Transactional注解

 @Nullableprotected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required./*** 判断当前方法是否是public修饰的,以及是否只支持public方法进行事务代理*/if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// First try is the method in the target class./*** 2.查找当前方法是否有添加@Transactional注解,如果有添加,就解析对应的注解信息* 下面有几个重复查找的动作,这里还没有搞明白依次获取到的是什么,总之都是判断入参的这个方法或者class有没有事务注解(大致的意思应该是先判断方法有没有添加注解,然后再判断类上是否添加事务注解)*/TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// Second try is the transaction attribute on the target class.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;}
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#determineTransactionAttributeorg.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)// 这个方法中调用了findMergedAnnotationAttributes来判断当前element是否有事务注解,
// 然后调用parseTransactionAnnotation,解析@Transactional注解的配置
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, Transactional.class, false, false);if (attributes != null) {return parseTransactionAnnotation(attributes);}else {return null;}}

这里是解析@Transactional注解对应的配置信息,并将配置信息存入到了一个TransactionAttribute对象中

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();Propagation propagation = attributes.getEnum("propagation");rbta.setPropagationBehavior(propagation.value());Isolation isolation = attributes.getEnum("isolation");rbta.setIsolationLevel(isolation.value());rbta.setTimeout(attributes.getNumber("timeout").intValue());rbta.setReadOnly(attributes.getBoolean("readOnly"));rbta.setQualifier(attributes.getString("value"));List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {rollbackRules.add(new RollbackRuleAttribute(rbRule));}for (String rbRule : attributes.getStringArray("rollbackForClassName")) {rollbackRules.add(new RollbackRuleAttribute(rbRule));}for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {rollbackRules.add(new NoRollbackRuleAttribute(rbRule));}for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {rollbackRules.add(new NoRollbackRuleAttribute(rbRule));}rbta.setRollbackRules(rollbackRules);return rbta;}

截止到这里,基本上就解析完毕了;如果有配置@Transactional注解,那么在最上面的methodMatcher.matches(method, targetClass) 方法就会返回true,返回true,那么spring后置处理器就任务当前类中有事务方法,需要进行增强,那就会生成代理对象,和aop的逻辑是一样的:
根据是否实现了接口,来判断是使用CGLIB代理还是使用JDK代理;所以:@Transactional注解就类似于aop中的切点(只有添加了@Transactional注解的方法,才会生成代理对象,在被调用的时候,进行事务拦截器进行处理)

spring事务应用

1.spring事务不支持非public方法的原因

如果在非public方法上添加事务注解,在发生异常的时候,事务是不会回滚的,也即:事务是不生效的

原因:

1.我们知道,spring事务其实就是利用了AOP动态代理的知识,也就是说:如果加了@Transactional注解的方法,spring会为其类生成代理对象,在调用的时候,会通过拦截器来调用
2.如果说spring不支持非public方法,那实现原理也简单:在判断是否需要进行动态代理的时候,首先判断下当前class对应的method是否是public的,如果是非public,就不进行后面的判断,直接返回false,无需代理即可;这样的话,就不会为类生成代理对象


下面这个方法在源码解析中有说到过,是bean在初始化的过程中,执行到第八个后置处理器的时候,调用的,判断是否需要对bean进行代理;在这个问题中,就不详细解释了

​ 我们只需要知道:如果这里返回的是null,就不会对该类和该方法进行增强,如果返回了TransactionAttribute对象,就会进行增强

org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute@Nullableprotected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// Don't allow no-public methods as required./*** 判断当前方法是否是public修饰的,以及是否只支持public方法进行事务代理*/if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// First try is the method in the target class./*** 2.查找当前方法是否有添加@Transactional注解,如果有添加,就解析对应的注解信息*/TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// Second try is the transaction attribute on the target class.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;}

针对这个问题,只需要关注这一行代码就可以

if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;
}/**
* 这里就是判断当前方法是否是public方法修饰的
*/
public static boolean isPublic(int mod) {return (mod & PUBLIC) != 0;
}org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#allowPublicMethodsOnly
@Override
protected boolean allowPublicMethodsOnly() {return this.publicMethodsOnly;
}在AnnotationTransactionAttributeSource这个类中,搜索可以发现,默认的都是true,并且这个类的初始化是在
ProxyTransactionManagementConfiguration中,在这个ProxyTransactionManagementConfiguration中,通过@Bean注解,注入了一个TransactionAttributeSource对象,@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}所以:这个参数铁定是true;也就是说,只允许public方法才能进行事务代理

spring事务源码-代理对象生成过程解析相关推荐

  1. Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析

      基于最新Spring 5.x,详细介绍了Spring 事务源码,包括< tx:annotation-driven/>标签源码解析.   此前我们已经学习了Spring的<tx:a ...

  2. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  3. Spring事务源码分析责任链事务链事务不生效

    文章目录 前言 带着问题分析源码 事务源码分析 寻找Spring事务源码类 TransactionInterceptor调用栈 分析Spring AOP责任链 分析TransactionInterce ...

  4. Spring事务源码(一)

    Spring事务源码(一) 前言 一.ProxyTransactionManagementConfiguration 二.BeanFactoryTransactionAttributeSourceAd ...

  5. Spring源码分析-Spring事务源码分析

    导语      在配置Spring事务管理的时候会用到一个类TransactionInterceptor,从下面的类关系图中可以看到TransactionInterceptor继承了MethodInt ...

  6. Spring事务源码详解

    一. 简介 事务: 事务是逻辑上的一组操作,要么都执行,要么都不执行,关于事务的基本知识可以看我的这篇文章:事务的基础知识 Spring事务: Spring 支持两种方式的事务管理:编程式事务管理.声 ...

  7. 【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  8. 足够应付面试的Spring事务源码阅读梳理

    来源:编程新说 Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答 ...

  9. 事务注解放到类上面 下面私有方法有效吗_【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)...

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  10. Spring事务源码分析

    首先看例子,这例子摘抄自开涛的跟我学spring3. @Test public void testPlatformTransactionManager() { DefaultTransactionDe ...

最新文章

  1. DCMTK:查询/检索服务类用户(C-MOVE操作)
  2. file_get_contents 在本地测试可以, 但在服务器上报错403
  3. 如何防止插入删除表造成的数据库死锁
  4. linux mysql 确认命令_LINUX启动/重启/停上MYSQL的命令(详解)
  5. 怎样给oracle表设置序列,Oracle的sequence序列在hibernate中的配置方法
  6. 【转】adb控台中Permission denied的解决方案
  7. 对于配置环境变量后的过程~
  8. SHELL脚本也要函数化
  9. 谷歌浏览器翻译显示服务器失败,谷歌浏览器翻译失败怎么办
  10. 浊度仪行业调研报告 - 市场现状分析与发展前景预测
  11. 国际音标 英式音标 美式音标
  12. 地图实时定位我的位置
  13. 思考力——提升企业竞争力的核心因素
  14. mac系统通过ADB与scrcpy实现手机投屏
  15. mac mysql docker_Mac上使用Docker如何快速启动MySQL测试
  16. 游戏建模3Dmax基础:游戏建模渲染小技巧
  17. web服务 面试可能会问的问题
  18. 苹果手机使用说明书_苹果手机11个使用小技巧
  19. 微型计算机汇编语言与接口技术答案,32位微机原理、汇编语言及接口技术教程课后习题答案.doc...
  20. 主板上的内存插槽颜色到底代表了什么意思?

热门文章

  1. 易筋SpringBoot 2.1 | 第四篇:RestTemplate方法详解(2)
  2. 文字描边加粗_这些PPT描边字,效果好到没朋友~
  3. Criteo数据集探索
  4. 简述环状引用问题及其解决方案
  5. python的符号lt和gt怎么输入_lt;lt;Python基础教程gt;gt;学习笔记 | 第04章 | 字典...
  6. 凸优化有关的数值线性代数知识 作业题
  7. mysql根据id主键查询是找到了就不再遍历后面的数据了吗_MySQL索引相关
  8. 关于表数据同步使用update_time时的注意事项
  9. Websocket 从header读取数据
  10. Idea修改项目的包名