查看原文
其他

Spring,为内部方法新起一个事务,此处应有坑

作者 | 等你归去来

来源 | cnblogs.com/yougewe/p/7466677.html


上一篇阿里巴巴正式取消“P”序列职级显示,”我爸爸是P9,你爸爸才P7”,这种梗终于一去不复返了


事务的作用,使我们操作能够连贯起来。而spring则是提供了一个更简单的方法,只要使用 @Transactional 一个注解,就可以保证操作的连贯性了。


普通用法,稍后再说,这里要说的是:在最外面的方法中,有一个@Transactional 的注解,当有抛出异常时,则进行回滚操作:


@Transactional(readOnly = false, rollbackFor = Throwable.class, isolation = Isolation.REPEATABLE_READ)


原本这个方法运行得好好的,但是有一天,我们需要在这个方法里添加一个新业务操作,而且这个业务操作是不要求回滚的,类似于做日志记录一类的。WHAT SHOULD I DO ?


由于业务的独特性,我能够快速想到的是,在这个类里面加一个private方法,然后直接去调用就ok了,如果说还是考虑到回滚的话,我也快速想到 @Transactional 的NOT_SUPPORTED传播特性,如:


@Transactional(propagation = Propagation.NOT_SUPPORTED)

private void doMyExJob(UserDebitCardBean userDebitCard) {

    System.out.println("do my job...");

    //do my job...

}


这看起来很合理,没毛病。


然而就是运行不起来,只要外面调用的方法一抛出异常,那么这个新方法的数据操作将会被回滚。妈蛋,到底哪里出了问题???仔细查了下资料,原来 @Transactional 注解由于原理决定了他只能作用于public方法中,而这里改为private,就完全被忽略无视了。OK,改呗:


@Transactional(propagation = Propagation.NOT_SUPPORTED)

public void doMyExJob(UserDebitCardBean userDebitCard) {

    System.out.println("do my job...");

    //do my job...

}


感觉应该好了,然而并没有。我也是醉了,这个问题,如果仔细花时间,找原理是没有问题的,但是在关键时刻来这么一下,还是很不爽的。网上看到一哥们说,还必须要将方法写到另一个类中,而且要通过spring的注入方式进行调用,才可以。好吧,那我就按照他的来,结果真的成功了。


//在接口中进行了定义,能够注入

@Override

@Transactional(propagation = Propagation.NOT_SUPPORTED)

public void doMyExJob(UserDebitCardBean userDebitCard) {

    System.out.println("do my job...");

    //do my job...

}


总算可以了,在赶时间的时候,能够解决问题的,就是好方法。至此,问题解决。1. 使用public访求;2. 写在外部类中,可被调用;3. 使用注入的方式进行该方法的执行。


说实话,spring这种事务还是有点不太好用的,要求太多,当然了,有很大部分原因是我没有理解其精髓。OK,下面我们来看看spring事务的讲解:


在配置文件中,默认情况下,<tx:annotation-driven>会自动使用名称为transactionManager的事务管理器。所以,如果定义的事务管理器名称为transactionManager,那么就可以直接使用<tx:annotation-driven/>。如下:


<!-- 配置事务管理器 -->

<beanid="transactionManager"

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"

    p:dataSource-ref="dataSource">

</bean>

 

<!-- enables scanning for @Transactional annotations -->

<tx:annotation-driven/>


关注顶级架构师公众号回复“Java”,送你一份Java面试题和答案惊喜礼包。


<tx:annotation-driven>一共有四个属性如下,


  • mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理

  • proxy-target-class:如果为true,Spring将创建子类来代理业务类;如果为false,则使用基于接口的代理。(如果使用子类代理,需要在类路径中添加CGLib.jar类库)

  • order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。

  • transaction-manager:指定到现有的PlatformTransaction Manager bean的引用,通知会使用该引用


@Transactional的属性


isolation 枚举org.springframework.transaction.annotation.Isolation的值 事务隔离级别


noRollbackFor Class<? extends Throwable>[] 一组异常类,遇到时不回滚。默认为{}


noRollbackForClassName Stirng[] 一组异常类名,遇到时不回滚,默认为{}


propagation 枚举org.springframework.transaction.annotation.Propagation的值 事务传播行为


readOnly boolean 事务读写性


rollbackFor Class<? extends Throwable>[] 一组异常类,遇到时回滚


rollbackForClassName Stirng[] 一组异常类名,遇到时回滚


timeout int 超时时间,以秒为单位


value String 可选的限定描述符,指定使用的事务管理器


@Transactional标注的位置

@Transactional注解可以标注在类和方法上,也可以标注在定义的接口和接口方法上。

如果我们在接口上标注@Transactional注解,会留下这样的隐患:因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务实现类继承。所以可能会出现不启动事务的情况。所以,spring建议我们将@Transaction注解在实现类上。

在方法上的@Transactional注解会覆盖掉类上的@Transactional。


注意:


@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。


虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。


默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。



公众号后台回复【架构】或者【架构整洁】有惊喜礼包!

架构师交流群

 「顶级架构师」建立了读者架构师交流群,大家可以添加小编微信进行加群

扫描添加好友邀你进架构师群,加我时注明姓名+公司+职位】


版权申明:内容来源网络,版权归原作者所有。如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。

猜你还想看

SpringBoot 三招组合拳,手把手教你打出优雅的后端接口
字节跳动一面:i++ 是线程安全的吗?
一个基于 Spring Boot 的项目骨架
JAVA 线上故障排查完整套路,从 CPU、磁盘、内存、网络、GC 一条龙!

长按识别图片二维码关注,订阅更多精彩

顶级架构师,企业架构、系统架构、网站架构、大规模分布式架构、高可用架构等架构讨论,以及结合互联网技术的架构调整。欢迎有想法、乐于分享的架构师交流学习

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存