首页 > 其他分享 >[Spring]事务失效之同一类内的方法调用

[Spring]事务失效之同一类内的方法调用

时间:2024-09-29 18:50:33浏览次数:6  
标签:事务 调用 Spring 代理 之同 失效 方法 public

在 Spring 中,事务是通过 AOP(面向切面编程)机制实现的。Spring 事务的管理是基于代理对象的,也就是说,Spring 会创建一个代理对象来拦截带有事务注解(如 @Transactional)的方法调用,并在方法执行前后进行事务的处理。因此,当某些情况下事务失效时,通常与 Spring 的代理机制有关。

具体来说,在同一类中的方法调用会导致 Spring 事务失效 的原因是 Spring 的代理机制无法拦截内部的自我调用(self-invocation),这破坏了事务的正确处理。以下是详细的解释:

1. Spring 事务的工作原理

Spring 使用两种代理机制来管理事务:

  • JDK 动态代理:针对实现了接口的类,Spring 会创建一个实现了相同接口的代理类。事务逻辑通过代理类在方法调用时插入。
  • CGLIB 代理:针对没有实现接口的类,Spring 会使用 CGLIB 生成子类代理,拦截方法调用并插入事务逻辑。

不论哪种代理方式,Spring 都是在代理类中对事务进行管理。如果调用来自外部的类,代理对象会拦截该调用并正确地管理事务逻辑。

2. 为什么同类中的方法调用导致事务失效

当一个类中的一个方法调用同类中的另一个方法时,如果调用的方法带有 @Transactional 注解,但这个调用是 直接的内部方法调用(不是通过代理对象调用的),Spring 事务将不会生效。这是因为内部方法调用不会通过 Spring 生成的代理类进行调用,而是直接在当前对象中执行,因此 Spring 无法介入处理事务。

例子:

@Service
public class TransactionService {

    @Transactional
    public void publicMethod() {
        // 事务在这里生效
        internalMethod();
    }

    @Transactional
    public void internalMethod() {
        // 事务在这里不会生效,因为它是通过内部调用触发的
    }
}

在这个例子中,当 publicMethod() 被外部调用时,Spring 事务管理器能够生效。然而,publicMethod() 内部直接调用了 internalMethod(),这属于类内部的自我调用。由于 internalMethod() 没有通过代理类进行调用,Spring 事务管理器无法对这个方法进行拦截和处理,导致事务失效。

3. 事务失效的原因总结

  • 内部调用:当类内部的方法调用另一个带有 @Transactional 注解的方法时,这个调用不会通过 Spring 的代理对象进行,而是直接通过 this 引用,因此 Spring 无法拦截并应用事务。
  • 代理对象失效:Spring AOP 代理机制只能拦截通过代理对象进行的方法调用,而不能拦截类内部的直接方法调用。

4. 如何解决同类中事务失效的问题

为了解决类内部方法调用导致的事务失效问题,可以考虑以下几种解决方案:

方法 1:通过 Spring 容器重新获取代理对象

在类内部通过 Spring 容器获取当前对象的代理实例,然后通过代理对象调用目标方法,从而让事务生效。

@Service
public class TransactionService {

    @Autowired
    private ApplicationContext context;

    @Transactional
    public void publicMethod() {
        // 从 Spring 容器中获取代理对象
        TransactionService proxy = context.getBean(TransactionService.class);
        proxy.internalMethod(); // 通过代理对象调用方法,事务生效
    }

    @Transactional
    public void internalMethod() {
        // 事务在这里生效
    }
}

方法 2:将事务性方法抽取到另一个类中

将需要事务管理的方法放在另一个 Spring 管理的类中,然后通过依赖注入的方式调用,这样每次调用都是通过代理对象进行的,事务管理可以生效。

@Service
public class TransactionService {

    @Autowired
    private AnotherService anotherService;

    public void publicMethod() {
        // 调用另一个类中的方法,事务生效
        anotherService.internalMethod();
    }
}

@Service
public class AnotherService {

    @Transactional
    public void internalMethod() {
        // 事务在这里生效
    }
}

方法 3:使用 AOP 编程手动管理事务

如果不想改变方法结构,也可以使用 Spring AOP 手动管理事务,显式地在方法内部控制事务的开始和结束。

@Service
public class TransactionService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void publicMethod() {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            internalMethod();
            transactionManager.commit(status);
        } catch (Exception ex) {
            transactionManager.rollback(status);
            throw ex;
        }
    }

    @Transactional
    public void internalMethod() {
        // 事务生效
    }
}

方法 4:使用 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)

使用 @EnableAspectJAutoProxy 并设置 exposeProxy = true,可以在内部调用时获取当前的代理对象,并通过代理对象调用方法。

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {}

@Service
public class TransactionService {

    @Transactional
    public void publicMethod() {
        // 获取代理对象
        TransactionService proxy = (TransactionService) AopContext.currentProxy();
        proxy.internalMethod(); // 通过代理对象调用方法
    }

    @Transactional
    public void internalMethod() {
        // 事务生效
    }
}

5. 总结

  • 原因:Spring 事务通过代理机制实现,当类中的一个方法调用同类中另一个带有 @Transactional 注解的方法时,由于这个调用没有经过代理对象,而是直接调用自身,Spring 无法拦截并应用事务,因此事务失效。
  • 解决方案:可以通过获取代理对象、将事务性方法移到其他类中,或者显式地管理事务来解决类内部调用导致的事务失效问题。

正确理解 Spring 的代理机制和事务管理原理,可以避免事务失效的问题并有效管理事务。

标签:事务,调用,Spring,代理,之同,失效,方法,public
From: https://www.cnblogs.com/DCFV/p/18440588

相关文章

  • 接上文实现SpringSecurity,拦截器的实现
    实现拦截器有图片可知,在上篇文章我们重写了UserDetailsManager,现在我们来进行之后的操作在UserDetailsManager中我们可以调动数据库去进行一个账号密码的校验之后我们这样设置拦截器进行一个token获取存储在usernamePasswordAuthenticationFilter这一层中,有,则存储在Secur......
  • 基于Springboot在线拍卖系统【附源码+文档】
    ......
  • spring 常见注解记录+ 使用自定义注解与aop 记录接口请求参数
    注解定义:importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importorg.springframework.core.annotation.Alias......
  • Springboot自定义Prometheus采集指标
    添加依赖<!--增加Prometheus依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>io.micrometer&l......
  • 基于Java+Springboot+Vue开发的大学竞赛报名管理系统源码+开发文章1.3万字
    项目简介该项目是基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践......
  • SpringBoot常用注解(超级详细,快快收藏)
    目录(1)@SpringBootApplication(2)@RestController(3)@RequestMapping(4)@GetMapping(5)@PostMapping(6)@PutMapping(7)@DeleteMapping(8)@RequestParam(9)@PathVariable(10)@RequestBody(11)@ResponseBody(12)@Autowired(13)@Component(14)@Service(15)@Reposit......
  • Spring boot 启动脚本
    #!/bin/bash#这里可替换为你自己的执行程序,其他代码无需更改APP_NAME=app.jarCONFIG_NAME=application.yml#使用说明,用来提示输入参数usage(){echo"Usage:sh执行脚本.sh[start|stop|restart|status]"exit1}#检查程序是否在运行is_exist(){pid=`ps......
  • Java毕业设计:基于Springboo咖啡厅座位预约网站毕业设计源代码作品和开题报告怎么写
     博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩。项目配有对应开发文档、开题报告、任务书、P......
  • Spring扩展接口
    InstantiationAwareBeanPostProcessor 是Spring框架提供的一个扩展接口,通过实现 InstantiationAwareBeanPostProcessor 接口,并重写其中的方法,可以在Spring容器实例化和初始化bean的各个阶段进行自定义处理,从而灵活地对bean进行定制化的操作,主要功能如下:实例化前......
  • springboot+vue+elementui大文件分片上传
    工具类方法:/***大文件分片上传*@paramfileName文件名*@paramfile文件*@paramfileKey文件key*@paramshardIndex当前分片下标*@paramshardTotal分片总量*/publicstaticvoidbigUpload(StringfileNam......