文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring 事务失效了,怎么办?

2024-12-01 19:10

关注

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

1. AOP 的原理

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

如果被代理的对象有接口,则可以使用 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 解决问题的。

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

小伙伴们看下,我的 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 中要开启循环依赖才能实现),大家在实际开发中,还是要从设计上尽量避免这种问题。

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

来源:江南一点雨内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯