共243行
2026-05-08 18:57:35
@Transactional 注解在以下情况会失效:publicthis 调用本类的其他方法(内部调用)try-catch 捕获且没有重新抛出@Service 等注解)public void add(ZyActivityAddParams params) {
ZyActivityPO po = new ZyActivityPO(params);
SpringUtil.getBean(ActivityServiceImpl.class).savePO(po, params.getActivityRuleIdList());
}
@Transactional(rollbackFor = Exception.class)
public void savePO(ZyActivityPO po, List<Long> activityRuleIdList) {
this.savePO(po);
Optional.ofNullable(po.getId())
.map(zyRelationActivityActivityRuleDAO::findByZyActivityId)
.ifPresent(zyRelationActivityActivityRuleDAO::deleteInBatch);
activityRuleIdList.stream()
.map(x -> new ZyRelationActivityActivityRulePO(po.getId(), x))
.forEach(zyRelationActivityActivityRuleDAO::save);
}
SpringUtil.getBean(ActivityServiceImpl.class).savePO(po, params.getActivityRuleIdList());从 Spring 容器中获取 ActivityServiceImpl 的代理对象,然后调用 savePO 方法。
this.savePO(...)?savePO 方法的特殊性(第292-304行):@Transactional(rollbackFor = Exception.class)
@NeedUpdateRecord(modal = "activity") // ← 自定义注解,用于记录更新日志
public void savePO(ZyActivityPO po, List<Long> activityRuleIdList) {
this.savePO(po); // 保存活动
// 删除旧的活动规则关联
Optional.ofNullable(po.getId())
.map(zyRelationActivityActivityRuleDAO::findByZyActivityId)
.ifPresent(zyRelationActivityActivityRuleDAO::deleteInBatch);
// 创建新的活动规则关联
activityRuleIdList.stream()
.map(x -> new ZyRelationActivityActivityRulePO(po.getId(), x))
.forEach(zyRelationActivityActivityRuleDAO::save);
}这个方法有两个重要的 AOP 切面:
@Transactional - 事务管理@NeedUpdateRecord - 自定义切面,记录操作日志this.savePO(...) 会怎样?// ❌ 错误写法
@Override
public void add(ZyActivityAddParams params) {
ZyActivityPO po = new ZyActivityPO(params);
// 通过 this 调用,Spring AOP 无法拦截!
this.savePO(po, params.getActivityRuleIdList());
}
// ✅ 正确写法
@Override
public void add(ZyActivityAddParams params) {
ZyActivityPO po = new ZyActivityPO(params);
// 通过 Spring 容器获取代理对象,AOP 切面可以正常拦截!
SpringUtil.getBean(ActivityServiceImpl.class).savePO(po, params.getActivityRuleIdList());
}| 调用方式 | 事务生效 | @NeedUpdateRecord 生效 | 说明 |
|---|---|---|---|
this.savePO(...) |
❌ 失效 | ❌ 失效 | 绕过 Spring AOP 代理 |
SpringUtil.getBean(...).savePO(...) |
✅ 生效 | ✅ 生效 | 通过代理对象调用 |
外部调用
↓
Spring 代理对象 (Proxy)
├─ 开启事务 (@Transactional)
├─ 记录操作日志 (@NeedUpdateRecord)
└─ 调用目标方法 savePO()
└─ 执行数据库操作
this 调用时:外部调用
↓
Spring 代理对象 (Proxy)
├─ 开启事务
├─ 记录日志
└─ 调用 add() 方法
└─ this.savePO() ← 直接调用目标对象的方法,绕过代理!
└─ ❌ 事务和日志切面都不生效
this 调用导致的事务失效问题当你需要在同一个类中调用带有 AOP 注解(如 @Transactional、@Cacheable、自定义注解等)的方法时,必须通过 Spring 容器获取代理对象,否则 AOP 切面不会生效。
类似的工具还有:
AopContext.currentProxy() - 获取当前代理对象@Autowired private ActivityServiceImpl self; - 然后通过 self.savePO(...) 调用你说得对!这是一个很好的观察。让我解释一下为什么没有报错:
SpringUtil.getBean() 和 AopContext.currentProxy() 的区别SpringUtil.getBean() - 不需要 exposeProxy = true ✅// 这种方式直接从 Spring 容器获取代理对象
SpringUtil.getBean(ActivityServiceImpl.class).savePO(po, params.getActivityRuleIdList());工作原理:
AopContextAopContext.currentProxy() - 需要 exposeProxy = true ❌// 这种方式从 ThreadLocal 获取当前代理对象
ActivityServiceImpl proxy = (ActivityServiceImpl) AopContext.currentProxy();
proxy.savePO(po, params.getActivityRuleIdList());工作原理:
ThreadLocal 中获取当前执行上下文的代理对象exposeProxy = trueCannot find current proxy| 特性 | SpringUtil.getBean() | AopContext.currentProxy() |
|---|---|---|
| 是否需要 exposeProxy | ❌ 不需要 | ✅ 必须 |
| 获取方式 | 从 IOC 容器 | 从 ThreadLocal |
| 性能 | 略慢(需要从容器查找) | 略快(直接从线程上下文获取) |
| 线程安全 | ✅ 安全 | ✅ 安全 |
| 推荐程度 | ⭐⭐⭐⭐ 推荐 | ⭐⭐ 不推荐 |
SpringUtil.getBean() 的方式@Override
public void add(ZyActivityAddParams params) {
ZyActivityPO po = new ZyActivityPO(params);
// ✅ 推荐:直接从容器获取代理对象
SpringUtil.getBean(ActivityServiceImpl.class).savePO(po, params.getActivityRuleIdList());
}优点:
AopContext.currentProxy()// ❌ 不推荐
@Override
public void add(ZyActivityAddParams params) {
ZyActivityPO po = new ZyActivityPO(params);
ActivityServiceImpl proxy = (ActivityServiceImpl) AopContext.currentProxy();
proxy.savePO(po, params.getActivityRuleIdList());
}缺点:
@EnableAspectJAutoProxy(exposeProxy = true)