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. linux里与Ctrl组合使用的键
  2. HTML和jQuery拆分成两个文件
  3. MySQL里的wait_timeout
  4. 使鼠标保持按住状态_MouseInc 鼠标手势工具
  5. 解决import keras后出现的一系列问题
  6. 初中毕业自考计算机专业难吗,初中学历自考本科难吗?初中毕业自考大专步骤有哪些?...
  7. Android应用开发相关下载资源
  8. php简单的mysql类_一个简单的php mysql操作类
  9. RocketMQ的安装与配置
  10. MYSQL分页优化查询
  11. js hover图片放大不遮挡_CSS3+JS 实现放大镜
  12. linux grep命令 例子,14个grep命令使用例子
  13. 新想法!华科、清华、康奈尔联合提出“圆形卷积”!聚合方形和圆形优势
  14. oracle 查看锁死的表
  15. Virtual Machine Manager 2012 R2利用服务模板部署SQL
  16. Java面向对象练习题之三角形
  17. Axure8.0如何汉化?
  18. 让LYNC安装更容易些
  19. msi b460m pro wifi黑苹果 efi
  20. 如何做到像百度云或者网易公开课一样动态更换APP启动图

热门文章

  1. 173.二叉搜索树迭代器
  2. 求小球落地5次后所经历的路程和第5次反弹的高度
  3. clear与resize
  4. JavaWeb程序的目录结构(2)
  5. 现代通信原理A.3:随机信号的功率谱估计
  6. idf逆文档频率为什么要用log??
  7. 《数学之美》阅读笔记(持续更新……)
  8. TopCoder交题方法
  9. 一行代码安装jupyter | 解决jupyter下载慢的问题
  10. ubuntu mysql5.5编码_Ubuntu下MySQL5.5编码设置