title: “spring同一个类中,一个方法调用另外一个注解(@Transactional)方法时,注解失效”

url: “https://wsk1103.github.io/”

tags:

  • 错误笔记

基于spring 3.2.9
参考 https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html

1. Transactional是什么

Transactional用来声明事务的,包括事务的begin和commit。被注解的public方法或者对象在被调用的时候,spring会为该public方法或者对象中的所有public方法生成一个代理对象来代理被注解的方法。

例如:
原类->

public class A {@Transactionalpublic void a() {...            }
}

被代理后

public class Proxy$A extend A{A a = new A();//spring扫描注解后,为注解的方法插入一个startTransaction()方法。public void a () {startTransaction();a.a();commitTransactionAfterReturning();}
}

2. 怎么使用@Transactional

目前比较流行的使用是基于Java注解声明。
分为2个步骤:

  1. 在spring.xml文件中声明事务的配置信息
    <!--======= 事务配置 Begin ================= --><!-- 事务管理器(由Spring管理MyBatis的事务) --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 关联数据源 --><property name="dataSource" ref="dataSource"></property></bean><!-- 注解事务 --><tx:annotation-driven/><!--======= 事务配置 End =================== -->
  1. 在public方法或者类上声明@Transactional

@Transactional 注解属性说明:

属性名 说明
value 当在xml配置文件中配置多个TransactionManager的时候,可以指定使用哪个事务管理器
propagation 事务的传播行为,默认为Propagation.REQUIRED(表示启动事务)。PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务,如果没有事务,则以非事务的方式继续进行。 PROPAGATION_NOT_SUPPORTED:以非事务的方法运行,如果当前存在事务,则将事务挂起。PROPAGATION_NEVER:以非事务的方法运行,如果当前存在事务,则抛出异常。
isolation 事务的隔离等级,默认为Isolation.DEFAULT。必须返回 TransactionDefinition 接口上定义的ISOLATION_XXX 常量之一。只有结合PROPAGATION_REQUIRED 或者 PROPAGATION_REQUIRES_NEW 一起声明才有意义。
timeout 默认值为 -1(不超时),单位秒。表示事务必须在规定的时间内处理完成,否则超时。
readOnly 默认false。该事务是否只读。
rollbackFor 用于指定能够触发回滚的异常类型。多个类型以,(英文逗号)隔开
rollbackForClassName 定义异常的名字,这些异常会触发回滚机制。多个类型以,(英文逗号)隔开
noRollbackFor 抛出异常,不回滚。多个类型以,(英文逗号)隔开
noRollbackForClassName 定义异常的名字,抛出异常,不回滚。多个类型以,(英文逗号)隔开

示例:

@Transactional(value = "transactionManager", timeout = 5, rollbackFor = {RuntimeException.class, NullPointerException.class},readOnly = true, propagation = Propagation.NOT_SUPPORTED)
public void b() {do something...
}

3. 实现机制

  1. spring默认使用AOP扫描被@Transactional的public方法,根据配置信息判断是否由TransactionInterceptor 进行拦截。
  2. TransactionInterceptor 进行拦截,在目标方法执行前创建事务,并执行目标方法。
  3. 根据sql执行情况,利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源DataSource ,执行提交或者回滚操作。

4. 问题重现

该问题是spring的AOP自调用引起的,注意文字开头说明。

public class A {a() {b();}//声明事务@Transactionalb() {sql操作}
}

如果这个时候直接通过调用a()方法,那么在b()方法运行错误的时候,是不会回滚代码的。原因如下:
类A会经过spring 中的AOP生成代理对象ProxyA

public class Proxy$A {A a = new A();a() {a.a();}b() {//开启事务startTransaction();a.b();}}

然后在运行的时候,是直接调用代理对象A(Proxy$A)中的a()方法,该a()方法直接调用原A类的a()方法,所以不会启动事务,最终导致事务失效。

5. 简单验证

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ConfigApp.class)
public class ATest {@Autowiredprivate A a;@Testpublic void run() {System.out.println("test a is:" + a.getClass().getName());a.run2(a);}}

代理类

package com.t;import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class A {@Transactional(rollbackFor = Exception.class)public void run1(A a) {System.out.println("run1 入参a:" + a.getClass().getName());System.out.println("run1 原a:" + this.getClass().getName());}public void run2(A a) {System.out.println("run2 入参a:" + a.getClass().getName());run1(a);System.out.println("run2 原a:" + this.getClass().getName());}}

结果输出

test a is:com.t.A$$EnhancerBySpringCGLIB$$c2ad1d54
run2 入参a:com.t.A$$EnhancerBySpringCGLIB$$c2ad1d54
run1 入参a:com.t.A$$EnhancerBySpringCGLIB$$c2ad1d54
run1 原a:com.t.A
run2 原a:com.t.A

结果简单说明
类A被代理后,会在代理对象ProxyA中声明一个新的对象A,并将A中d对应的方法重新封装。当调用原A的方法时,流程就会变成 proxyA.a() -> a.a()

5. 解决方法

  • 第一种:将b()方法抽出来,重新声明一个类,并且该类交由spring管理控制。
  • 第二种:同时在a()上添加@Transactional注解或者在类上添加。
  • 第三种:在原A类中的a()方法,改为 ((A)AopContext.currentProxy).b()

spring同一个类中,一个方法调用另外一个注解(@Transactional)方法时,注解失效相关推荐

  1. Spring - 同一个类中的方法互相调用,注解失效问题的分析和解决(转)

    Spring - 同一个类中的方法互相调用,注解失效问题的分析和解决(转) 参考文章: (1)Spring - 同一个类中的方法互相调用,注解失效问题的分析和解决(转) (2)https://www. ...

  2. Spring同一个类中注解方法互相调用的问题

    在使用Spring时,很多初学者不了解Spring对象注入的机制和面向切面编程的原理,很容易犯一些错误.下面就是初学者最容易犯的错误.举例如下: @Component public class Tes ...

  3. SpringCache @Cacheable 在同一个类中调用方法,导致缓存不生效的问题及解决办法...

    由于项目需要使用SpringCache来做一点缓存,但自己之前没有使用过(其实是没有听过)SpringCache,于是,必须先学习之. 在网上找到一篇文章,比较好,就先学习了,地址是: https:/ ...

  4. Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法

    Spring 事务机制回顾 Spring事务一个被讹传很广说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务.  结果造成开发人员在设计事务方法时束手束脚,生怕一不小心就踩到地雷.    ...

  5. @transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!...

    Spring注解(如@Transactional.@Cacheable.@Async等),在使用不当时,很可能会失效.失效的情况有很多种,本文我们就来瞅瞅,为啥同一个类中普通方法调用Spring注解方 ...

  6. 就同一个Service类中,一个事务方法调用另外一个有事务的方法

    目录 一.Spring 事务机制 二.Spring事务传播行为 三.场景总结 1.在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解是不会生效的 2. ...

  7. Spring中同一个类中方法调用事务不生效,非事务方法调用事务方式事务不生效

    我们假定在SerivceXXX中有两个方法: serviceA 非事务方法 serviceB事务方法 如果serviceA中方法定义类似如下: public void serviceA(){..... ...

  8. 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法

    在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法 参考文章: (1)在同一个类中,一个方法调用另外一个有注解(比如@Async, ...

  9. 【Python——类】 同一个类中一个函数里调用另一个函数的方法

    [Python-类] 同一个类中一个函数里调用另一个函数的方法 class Solution:def a(self):self.b() # 注意这种写法:self.类名def b(self):prin ...

最新文章

  1. 8953n的user获取权限以及remount
  2. dubbo启动时检查服务
  3. Executor家族的辨析
  4. Rake::TestTask 介绍
  5. mysql为数据库表起别名的注意事项
  6. 工业以太网交换机的安全问题详解
  7. ps海报合成教程_如何利用PS制作海报?详细教程来了!
  8. 四元数组旋转_四元数应用——顺序无关的旋转混合
  9. 在Sql Server 2008上安装SDE 9.3
  10. 如何扩大网站访问,五种方法让问题不再难
  11. 《Head First 设计模式》(一):策略模式
  12. Stata15重编码
  13. 比湿和相对湿度的转换、体感温度的计算
  14. [BZOJ1921] [CTSC2010]珠宝商
  15. sci影响因子小于1计算机,影响因子小于1的期刊_影响因子_柳叶刀影响因子多少...
  16. ps修改头发颜色----和修改衣服颜色-------给褶皱的衣服添加图案
  17. Hadoop,master和slave简单的分布式搭建
  18. 如何将多行和多列转换为行和行Excel
  19. Android 应用换肤方案的总结
  20. 计算机鼠标不会动,鼠标灯亮着却不会动怎么办 电脑鼠标动不了解决方法

热门文章

  1. 关于String字符串的比较
  2. 计算机操作系统选择题
  3. matlab空间曲面拟合,【Matlab】离散点拟合曲面
  4. Ubuntu安装落雪音乐LX Music
  5. js实现金额的大写转小写
  6. 数字化医院PACS影像系统 三维影像后处理技术应用
  7. 教你cad版本怎么用转换器转换操作
  8. 电子英汉词典c语言程序设计报告,英汉电子词典设计报告_设计_C语言_C语言程序设计.doc...
  9. c语言词典课程设计报告,英汉电子词典C语言课程设计
  10. 有了这 15 款编程游戏,谁都可以学编程!