当前位置: 首页 > news >正文

spring 声明式事务方法自调用问题

1.spring 声明式事务方法自调用问题

这是我自己在学习sprng 声明式事务时遇到我的问题

  • 话不多说,先上问题代码
@Transactional()public void changeAge() {studentDao.updateAgeById(99,2);}/*** 声明两个独立修改数据库的事务业务方法*/@Transactional()public void changeName() {studentDao.updateNameById("zg999",2);//int i= 1/0;}@Transactionalpublic void changAgeAndName(){changeAge();changeName();}
//最下面这个方法的注解会失效吗,还是上面的会失效

在Spring框架中,使用@Transactional注解可以声明一个方法或类需要在事务的上下文中执行。然而,当你将@Transactional注解应用于一个方法(如changAgeAndName),并且这个方法内部调用了其他带有@Transactional注解的方法(如changeAgechangeName)时,Spring默认不会为这些内部调用创建新的事务或加入当前事务的上下文中。

这是因为Spring的@Transactional注解是基于AOP(面向切面编程)实现的,而AOP代理通常只在外部调用时才会生效。当changAgeAndName方法内部调用changeAgechangeName时,这些调用并不是通过代理对象进行的,因此Spring不会为这些内部调用应用事务管理。

在你的例子中,changAgeAndName方法中的changeAge()changeName()调用并不会因为它们各自被@Transactional注解而自动加入到一个单独的事务中。实际上,它们会被当作普通的Java方法调用执行。这意味着:

  1. 如果changeAge()changeName()内部发生异常(比如取消注释的int i= 1/0;),并且这个异常没有被捕获和重新抛出到changAgeAndName方法之外,那么这个异常将只会在内部方法中处理(如果有异常处理的话),而不会导致changAgeAndName方法的事务回滚。

  2. changAgeAndName方法本身的事务性(如果有的话)将不会受到changeAge()changeName()中事务性行为的影响。如果changAgeAndName被声明为@Transactional,那么它的执行将作为一个单独的事务,而changeAge()changeName()的调用将不会触发任何新的事务或影响这个事务的边界。

要解决这个问题,你有几个选项:

  • 使用编程式事务管理:在changAgeAndName方法中显式地管理事务,使用TransactionTemplatePlatformTransactionManager
  • 重新设计应用架构:避免在事务性方法中调用其他事务性方法,而是通过服务层或其他机制来协调这些操作。
  • 使用@Transactional(propagation = Propagation.REQUIRES_NEW):虽然这不适用于changAgeAndName内部调用changeAge()changeName()的场景,但它可以用于需要每个方法独立事务的情况。

总之,在你的例子中,changAgeAndName方法中的@Transactional注解将仅影响该方法本身作为一个独立事务的执行,而不会对changeAge()changeName()的调用产生额外的事务性影响。

接着我又去查了一下关于aop代理和内部调用的问题

Srping-AOP代理和内部调用问题

AOP(Aspect-Oriented Programming,面向切面编程)代理和内部调用问题是Spring框架中一个重要的概念,它涉及到Spring AOP的实现机制以及事务管理、日志记录、安全检查等横切关注点(cross-cutting concerns)的应用。下面分别解释AOP代理和内部调用问题。

AOP代理

AOP代理是Spring AOP实现机制的核心。在Spring中,AOP代理通过创建一个代理对象来包装原始对象(即被增强的目标对象),并在代理对象中嵌入横切逻辑(如事务管理、日志记录等)。当外部代码通过代理对象调用目标对象的方法时,代理对象会在目标方法执行前后插入横切逻辑,从而实现对目标方法的增强。

Spring AOP支持两种代理机制:

  1. JDK动态代理:适用于实现了接口的类。Spring通过Java的反射API为接口生成一个实现类,这个实现类就是代理类,它实现了与目标对象相同的接口,并在调用接口方法时加入横切逻辑。

  2. CGLIB代理:适用于没有实现接口的类。Spring使用CGLIB库为目标类生成一个子类,这个子类就是代理类,它继承了目标类并重写了目标类的方法,在方法调用时加入横切逻辑。

内部调用问题

内部调用问题是指在同一个类的内部,当一个方法调用该类中的另一个方法时,如果这两个方法都被@Transactional或其他AOP注解标记,那么AOP代理可能不会生效。这是因为内部调用是通过this引用直接进行的,而不是通过Spring容器管理的代理对象进行的。由于AOP代理的逻辑是在代理对象的方法调用过程中插入的,因此直接通过this引用调用方法会绕过代理对象,从而导致AOP注解失效。

解决方法

为了解决内部调用导致的AOP代理失效问题,可以采取以下几种方法:

  1. 将方法提取到不同的Bean中:将需要应用AOP逻辑的方法提取到不同的Spring Bean中,然后通过Spring容器来管理这些Bean之间的依赖关系。这样,当一个Bean的方法调用另一个Bean的方法时,调用就会通过Spring的代理对象进行,从而确保AOP逻辑能够生效。

  2. 使用ApplicationContext.getBean()获取代理对象:在需要调用同类中其他方法的地方,通过ApplicationContext.getBean()方法获取当前Bean的代理对象,然后通过代理对象来调用方法。这种方法虽然可以实现AOP逻辑的应用,但会破坏类的封装性,增加代码的复杂度。

  3. 使用@EnableAspectJAutoProxyAopContext.currentProxy():通过在配置类上添加@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)注解,并在需要的地方使用AopContext.currentProxy()来获取当前类的代理对象,然后通过代理对象来调用方法。这种方法要求Spring AOP的底层实现支持AspectJ的代理方式,并且需要确保exposeProxy属性被设置为true以允许在类内部访问代理对象。

  4. 重新设计应用架构:在可能的情况下,重新设计应用架构以避免内部调用导致的AOP代理失效问题。例如,可以通过将业务逻辑拆分为更小的服务或组件,并通过服务层来协调这些组件之间的交互,从而确保每个组件都可以独立地应用AOP逻辑。

内部调用问题的详情说明

这里用例子解释一下
在Spring框架中,当使用AOP(面向切面编程)技术,特别是与@Transactional注解结合使用时,确实存在内部调用问题(也称为“自调用问题”)。这个问题主要发生在同一个类中的方法互相调用时,如果这些方法都被@Transactional或其他AOP注解标记。

具体来说,当一个方法(我们称之为方法A)通过this引用调用同一个类中的另一个方法(方法B),并且这两个方法都被@Transactional注解时,问题就显现了。这是因为Spring AOP的代理机制默认是基于接口的(对于JDK动态代理)或基于类的(对于CGLIB代理),但无论是哪种情况,它都依赖于代理对象来拦截方法调用,并在方法调用前后插入额外的逻辑(如事务管理)。然而,当方法A通过this引用调用方法B时,调用是直接在类的实例上进行的,而不是通过Spring容器创建的代理对象。因此,代理对象上的AOP逻辑(如事务管理)不会被触发,导致@Transactional注解在方法B上失效。

要解决这个问题,有几种策略:

重构代码:将需要事务管理的方法移动到另一个bean中,并通过依赖注入的方式调用这些bean中的方法。这样,每次调用都会通过Spring的代理对象进行,从而确保AOP逻辑生效。
使用AspectJ的编译时或加载时织入:虽然这超出了Spring AOP的范围,但AspectJ提供了更强大的AOP支持,包括编译时和加载时织入,可以在不依赖于代理的情况下实现AOP。
手动控制事务:在某些情况下,可以通过编程方式(如使用TransactionTemplate)手动控制事务的边界,而不是依赖@Transactional注解。
使用self-invocation属性(不适用于Spring AOP):需要注意的是,这个属性是Spring声明式事务管理中的一个概念,但它并不适用于基于AOP的@Transactional注解。这个属性主要用于控制基于XML配置的事务管理器的行为,与这里的讨论不直接相关。
总之,当在Spring中使用AOP和@Transactional注解时,需要特别注意内部调用可能导致AOP逻辑失效的问题,并采取相应的策略来避免或解决这一问题。

本人还处在学习阶段,对于资料的查找,笔记的书写难免存在错误之处,还希望有大佬不吝赐教


http://www.mrgr.cn/news/11895.html

相关文章:

  • Excel技能分享:不同sheet间根据值匹配进行赋值
  • @JsonFormat和@JSONField分别的使用场景
  • 年轻人喝不起蜜雪冰城了
  • 英特尔终止开发开源 H.265/HEVC 编码器项目
  • 【Java日志系列】Log4j2日志框架
  • 蓝奥声4G系列新突破:4G+RS485+蓝牙+WiFi+以太网综合智能网关,多元智控!
  • 事件相关介绍
  • SQL进阶技巧:如何按任意时段分析时间区间问题? | 分区间讨论【左、中、右】
  • 万邑通信息科技笔试题库:北森测评言语数字图形真题答题要求及真题分享
  • 学习大数据DAY44 帆软 report 配置
  • (转载)使用zed相机录制视频
  • VLDB 2024 即将来袭!创邻科技将带来精彩分享
  • 【C语言】深入理解指针3(附转移表源码)
  • 外包干了两年,快要废了。。。
  • SE11 没有激活的名称表存 No active nametab exists for
  • SQL 优化实践:从慢查询到高性能更新
  • C语言 | Leetcode C语言题解之第375题猜数字大小II
  • ShareSDK 企业微信
  • [数据集][目标检测]电力场景红外图像输电线路绝缘子检测数据集VOC+YOLO格式1846张1类别
  • Java 面试题:在浏览里输入URL后回车会发生什么--xunznux