Spring获取当前类的代理类(用于类内部方法调用事务不生效的情况)

本文探讨了在Spring中如何通过@Autowired、@Resource注解或BeanFactory来获取并使用事务代理对象,强调了@EnableAspectJAutoProxy(exposeProxy=true)的重要性。文章解释了为何需要设置exposeProxy为true,因为代理对象通常存储在ThreadLocal中,并展示了获取代理对象的代码片段。同时,提到了在使用Spring声明式事务时应注意事务的传播特性,以避免潜在问题。
Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

1. 使用@Autowired注解或者@Resource注解注入自己,然后使用注入的代理类去调用事务方法。如下:

2. 注入BeanFactory容器,使用容器获取代理类,然后使用代理类调用事务方法。如下:

3. 在类上面添加注解@EnableAspectJAutoProxy(exposeProxy = true),然后在类中获取当前类的代理类,使用代理类调用事务方法。如下:

注:如果不添加注解的话,可能会出现以下错误

java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.  

解析:为什么需要exposeProxy = true

  1. 代理对象的获取其实是从线程的ThreadLocal中获取的,如下:
  2. 对象是什么时候放进去的呢,查看JdkDynamicAopProxy和CglibAopProxy,其中有以下代码,所以注解中需要先置为true才会将代理对象设置进去。

备注:使用Spring的声明式事务时需要注意事务的传播特性,避免使用时出现问题。

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

<think>我们正在讨论Spring中同一个内部方法调用导致事务不生效的问题。根据引用内容,核心原因是自调用(即在同一个中一个方法调用另一个方法)绕过了代理对象,导致事务拦截器没有被执行。 根本原因: Spring事务管理(以及AOP增强)是通过代理对象实现的。当我们调用一个被@Transactional注解的方法时,实际上是通过代理对象调用的,代理对象会在方法执行前开启事务,在方法执行后提交或回滚事务。然而,如果在同一个中,一个非事务方法(如A)直接调用同一个中的事务方法(如B),那么这里的调用是通过`this`(即当前对象本身)来调用的,而不是通过代理对象。因此,事务拦截器没有机会介入,事务自然不会生效。 具体分析(结合引用): 引用[1]指出:“同一个中,一个无事务方法调用另一个有事务方法事务不生效。主要原因是Spring数据库事务的约定,其实现原理是AOP,而AOP的原理是动态代理,在自调用的过程中,是自身的调用,而不是代理对象调用,那么就不会产生AOP”。 引用[2]和[3]都通过代码示例说明了这个问题:在内部通过`this`调用事务方法,实际上并没有经过代理,因此事务不会生效。 引用[4]同样指出:“spring项目中,只有通过代理对象调用方法才会被事务管理器管理,同一个方法调用不会触发代理的”。 解决方法: 1. 使用AopContext.currentProxy()获取当前代理对象,然后通过代理对象调用方法(需要开启AspectJ代理支持): ```java @Service public class MyService { public void aMethod() { // 通过代理对象调用bMethod ((MyService) AopContext.currentProxy()).bMethod("zhangsan", 12); } @Transactional(rollbackFor = Exception.class) public void bMethod(String name, Integer age) { // ... } } ``` 注意:需要在配置上添加`@EnableAspectJAutoProxy(exposeProxy = true)`以暴露代理对象。 2. 将事务方法抽取到另一个Bean中,通过注入该Bean来调用(推荐): ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; public void aMethod() { serviceB.bMethod("zhangsan", 12); } } @Service public class ServiceB { @Transactional(rollbackFor = Exception.class) public void bMethod(String name, Integer age) { // ... } } ``` 3. 通过注入自身Bean来调用(注意避免循环依赖): ```java @Service public class MyService { @Autowired private MyService self; // 注入自身代理对象 public void aMethod() { self.bMethod("zhangsan", 12); // 通过注入的代理对象调用 } @Transactional(rollbackFor = Exception.class) public void bMethod(String name, Integer age) { // ... } } ``` 4. 使用编程式事务(TransactionTemplate): ```java @Service public class MyService { @Autowired private TransactionTemplate transactionTemplate; public void aMethod() { // ... 非事务操作 transactionTemplate.execute(status -> { // 事务操作 bMethod("zhangsan", 12); return null; }); } // 注意:bMethod方法上不需要@Transactional注解 public void bMethod(String name, Integer age) { // ... } } ``` 总结:要解决同一个方法调用导致事务不生效的问题,核心在于确保调用事务方法时是通过代理对象进行的,而不是直接通过`this`调用。上述方法中,推荐使用第二种(抽取到另一个Bean)和第三种(注入自身)方法,因为它们更符合Spring的设计理念,且代码清晰。 §§相关问题§§ 1. 如何理解 Spring AOP 中的自调用问题? 2. 使用 AopContext.currentProxy() 方法有哪些注意事项? 3. Spring 事务传播行为在跨 Bean 调用时是如何工作的? 4. 为什么编程式事务可以解决自调用事务失效问题? 5. 在 Spring 中,如何避免循环依赖的同时解决自调用事务失效? </think> ### 直接调用方法导致事务不生效的原因 在 Spring 中,直接通过 `this.方法()` 调用中的事务方法会导致事务失效,核心原因是**绕过了 Spring 的代理机制**。具体分析如下: #### 1. **事务实现的原理(基于动态代理)** - Spring事务管理是通过 **AOP 动态代理**实现的。 - 当调用 `@Transactional` 方法时,实际调用的是**代理对象**的方法(如 `$Proxy123.bMethod()`),代理对象会在方法执行前后添加事务逻辑(开启/提交事务)[^1][^3]。 - 示例流程: ```java // 代理对象内部逻辑(简化) public Object proxyMethod() { 开启事务(); // 代理对象添加事务逻辑 target.bMethod(); // 调用原始对象的方法 提交事务(); // 代理对象添加事务逻辑 } ``` #### 2. **自调用问题(关键原因)** - **直接调用 `this.bMethod()` 时**: - `this` 指向的是**原始对象**(如 `UserServiceImpl`),而非代理对象[^2]。 - 原始对象内部没有事务处理逻辑,因此 `@Transactional` 注解被忽略。 - 相当于直接执行裸方法,无事务管理[^4]。 - 示例代码说明: ```java public class UserService { public void aMethod() { this.bMethod(); // ❌ this是原始对象,事务失效 } @Transactional public void bMethod() { /* 数据库操作 */ } } ``` #### 3. **事务失效的直接后果** - **无事务边界**:方法不会在事务中执行,数据库操作直接提交,无法回滚[^1]。 - **传播行为失效**:例如 `@Transactional(propagation = Propagation.REQUIRES_NEW)` 不会创建新事务。 #### 4. **解决方案(确保通过代理对象调用)** - **注入自身代理**(推荐): ```java @Service public class UserService { @Autowired private UserService self; // 注入代理对象 public void aMethod() { self.bMethod(); // ✅ 通过代理对象调用 } } ``` - **使用 `AopContext`**(需开启配置): ```java // 配置添加 @EnableAspectJAutoProxy(exposeProxy = true) // 调用((UserService) AopContext.currentProxy()).bMethod(); ``` - **抽取到独立 Bean**: ```java @Service public class ServiceA { public void aMethod() { /* 非事务逻辑 */ } } @Service public class ServiceB { @Transactional public void bMethod() { /* 事务逻辑 */ } } ``` #### 总结 | **场景** | **事务生效** | **原因** | |------------------------|-------------|------------------------------| | `proxy.bMethod()` | ✅ | 通过代理对象调用,触发 AOP 逻辑 | | `this.bMethod()` | ❌ | 绕过代理,直接调用原始方法 | > 关键点:Spring 事务的本质是代理增强,**只有通过代理对象调用方法才会被事务管理器拦截**。自调用(`this.方法()`)破坏了代理链,导致事务失效[^4]。 --- ### 相关问题 1. **如何验证 Spring 中某个方法是否通过代理对象执行?** 2. **使用 `@Transactional` 时,哪些场景会导致事务意外失效?** 3. **Spring 代理对象和原始对象在内存中的结构有何区别?** 4. **为什么通过 `this` 调用 `@Async` 异步方法也会失效?** 5. **如何在不注入自身的情况下解决同一中的事务调用问题?**
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值