热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring事务失效了,怎么办?:spring事务在哪些情况下失效

本文主要分享【spring事务在哪些情况下失效】,技术文章【Spring事务失效了,怎么办?】为【_江南一点雨】投稿,如果你遇到关于SpringBoot相关问题,本文相关知识或能到你。sprin

本文主要分享【spring事务在哪些情况下失效】,技术文章【Spring 事务失效了,怎么办?】为【_江南一点雨】投稿,如果你遇到关于Spring Boot相关问题,本文相关知识或能到你。

spring事务在哪些情况下失效

这是小伙伴们在微信上问的一个问题:

这个问题比较典型,让我想到面试时有一个 Spring 事务失效的问题,跟这个原因以及解决方案是一模一样的,因此,抽空整篇文章和小伙伴们分享下。

1. AOP 的原理

小伙伴们知道,AOP 底层就是动态代理,动态代理有两种实现方式:

JDK 动态代理:利用拦截器(必须实现 InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。举个例子,假设有一个接口 A,A 有一个实现类 B,现在要给 B 生成代理对象,那么实际上是给 A 接口自动生成了一个匿名实现类,并且在这个匿名实现类中调用到 B 中的方法。CGLIB 动态代理:利用 ASM 框架,对代理对象类生成的 class 文件加载进来,通过修改其字节码生成子类来处理。举个例子,现在有一个类 A,A 没有接口,现在想给 A 生成一个代理对象,那么实际上是自动给 A 生成了一个子类,在这个子类中覆盖了 A 中的方法, 所以,小伙伴们要注意,A 类以及它里边的方法不能是 final 类型的,否则无法生成代理

如果被代理的对象有接口,则可以使用 JDK 动态代理,没有接口就可以使用 CGLIB 动态代理。

在 Spring 中,默认情况下,如果被代理的对象有接口,就使用 JDK 动态代理,如果被代理的对象没有接口,则使用 CGLIB 动态代理。

在 Spring Boot 中,2.0 之前也跟 Spring 中的规则一样,2.0 之后则统一都使用 CGLIB 动态代理。

不过这些都是默认的规则,如果有接口,但是你又希望使用 CGLIB 动态代理,通过修改配置,也都是可以实现的:

如果是 XML 配置,想使用 CGLIB 动态代理,可以按如下方式实现:

<aop:config proxy-target-class="true">
    <aop:pointcut id="pc1" expression="。。。"/>
    <aop:aspect ref="logAdvice">
        。。。
    
     aop:aspect> 
      aop:config> 

如果是 Java 配置,想使用 CGLIB 动态代理,可以按如下方式实现:

@Component
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class LogAspect {
   
}

当然,在新版 Spring Boot 项目中,有接口的类默认就是使用 CGLIB 动态代理的。但是此时如果有接口的类你又想使用 JDK 动态代理,那么可以通过如下配置:

spring.aop.proxy-target-class=false

关于 Spring Boot 中的 AOP 代理问题,可以参考去年松哥写的文章:
Spring Boot 中的 AOP,到底是 JDK 动态代理还是 Cglib 动态代理?。

2. 实际用的类

基于第一小节的讲解,小伙伴们知道,当你在项目中用到了 AOP 之后,其实你所以见到的类,并不是原本的类了。

松哥前面写了好几篇 AOP 相关的文章,如下:

手把手教你玩多数据源动态切换!Redis 做接口限流,一个注解的事!处理接口幂等性的两种常见方案|手把手教你数据权限,一个注解搞定!

虽然是解决不同的问题,但是有一个共同的点,那就是都是通过自定义注解+ AOP 解决问题的。

现在我就以手把手教你玩多数据源动态切换!为例,来和大家说说这里的动态代理到底是咋回事,没看过这篇文章的小伙伴可以先看下。

小伙伴们看下,我的 UserService 大致上是下面这样:

@Service
public class UserService {
   

    @Autowired
    UserMapper userMapper;

    @DS("master")
    public Integer count() {
   
        return userMapper.getCount();
    }
}

小伙伴们看到,count() 方法上加了 @DS 注解,所以这个 count() 方法将来是要被自动代理的。换言之,当你在另外一个类中注入 UserService 的时候,其实不是这个 UserService,我 DEBUG 小伙伴们来看一下:

小伙伴们从图中可以看到,此时我注入的 UserService 并不是真正的 UserService,而是一个通过 CGLIB 动态代理为 UserService 生成的子类,这个子类里边的 count 方法大致逻辑类似下面这样(其实就是 AOP 中的代码,具体小伙伴们可以参考 手把手教你玩多数据源动态切换!一文):

# 切换数据源
# 去数据库查询 count
# 清空 ThreadLocal 中的变量
# ...

但是,如果我的调用逻辑是这样呢:

@Service
public class UserService {
   

    @Autowired
    UserMapper userMapper;

    public Integer count2() {
   
        return count();
    }

    @DS("master")
    public Integer count() {
   
        return userMapper.getCount();
    }
}

小伙伴们来看,count2 方法,这个时候直接在 count2 方法中调用了 count 方法,当然,count2() 方法中的调用也可以写作 this.count();,这样看起来就更明确了,我们调用 count 方法,使用的是当前对象,而当前对象是不包含代理对象中的代码的,我们通过 DEBUG 来看下:

所以,当我们在 count2 中直接调用 count 方法的时候,那么加在 count 方法上的注解就会失效。

3. 问题解决

这个问题存在于所有使用了 AOP 的地方,存在的原因第二小节已经分析的很清楚了。

解决办法其实也有很多种,最为简单省事的一种,就是在当前类中注入代理对象,然后通过代理对象去调用其他方法,如下:

@Service
public class UserService {
   

    @Autowired
    UserMapper userMapper;

    @Autowired
    UserService userService;

    public Integer count2() {
   
        return userService.count();
    }

    @Transactional
    @DS("master")
    public Integer count() {
   
        return userMapper.getCount();
    }

}

虽然问题解决了,不过这毕竟不是一个好的解决办法(因为自己中注入自己,在新版 Spring Boot 中要开启循环依赖才能实现),大家在实际开发中,还是要从设计上尽量避免这种问题。

好啦,这个问题搞明白了,那么事务失效这个问题,也不用我多说了吧!

本文《Spring 事务失效了,怎么办?》版权归_江南一点雨所有,引用Spring 事务失效了,怎么办?需遵循CC 4.0 BY-SA版权协议。


推荐阅读
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • andr ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文详细介绍了如何使用Spring Boot进行高效开发,涵盖了配置、实例化容器以及核心注解的使用方法。 ... [详细]
  • 本文详细介绍了如何解决MyBatis中常见的BindingException错误,提供了多种排查和修复方法,确保Mapper接口与XML文件的正确配置。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
author-avatar
张梦蒙4428
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有