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

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

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

  • 话不多说,先上问题代码
@Transactional()
    public void changeAge() {
        studentDao.updateAgeById(99,2);

    }


    /**
     * 声明两个独立修改数据库的事务业务方法
     */
    @Transactional()
    public void changeName() {
        studentDao.updateNameById("zg999",2);
        //int i= 1/0;
    }

    @Transactional
    public 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逻辑失效的问题,并采取相应的策略来避免或解决这一问题。

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

  • 16
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值