Spring 嵌套方法AOP不生效问题

本文探讨了Spring AOP的两种实现方式及其限制。通过示例说明了当从一个方法内部调用另一个方法时,目标方法上的AOP拦截可能不会生效的问题,并给出了两种解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述, 如下Abc定义为一个Bean, b()方法添加@TargetDatasource,定义切面DynamicDataSourceAspect,期望:调用a()方法,b()方法上的AOP拦截能生效。实际不生效。

@Service

public class Abc {

public void a(){
b();
}

@TargetDatasource(value=abc) public void b(){
} }

 

AOP代码:

@Order(-1)
@Component
@Slf4j
@Aspect
public class DynamicDataSourceAspect {

  /**
   * set data source value.
   */
  @Before(value = "@annotation(targetDataSource)", argNames = "targetDataSource")
  public void setDataSource(TargetDataSource targetDataSource) {
    if (targetDataSource != null && targetDataSource.value() != null) {
      log.info("Databse value: {}", targetDataSource.value());
      DynamicDataSourceHolder.set(targetDataSource.value());
    }
  }

  /**
   * clear data source value.
   */
  @After(value = "@annotation(targetDataSource)", argNames = "targetDataSource")
  public void clearDataSource(TargetDataSource targetDataSource) {
    DynamicDataSourceHolder.clear();
  }
}

 

问题分析:

我们都知道Spring aop有两种实现方式,基于Interface生成代理和cglib生成代理。上面的例子中,当调用a()方法时,调用者拿到的是Abc的代理类,即增强类。如果a()方法上有@TargetDatasource注解,拦截会生效。然而,在a()方法里调用b(),b方法上的拦截不会生效。原因是因为a()调用b(), 用的是b()方法的目标类,而不是代理类,所以拦截不生效。

 

解决方法:

1. 重构代码, 把b()方法移到一个Bean里面。

2. 调整a()方法如下:

@Service

public class Abc {

public void a(){
   ((Abc)AopContext.currentProxy()).b();
}

@TargetDatasource(value=abc)
public void b(){
}
}

 

Sping 官方解释 - https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop 

转载于:https://www.cnblogs.com/lzmrex/p/9450911.html

### Spring框架中嵌套调用的实现与问题解决 #### 1. 嵌套调用的概念及其挑战 在Spring应用程序中,当一个服务方法内部调用了另一个同属该类的方法时,这种行为被称为嵌套调用。然而,在某些情况下,特别是涉及到面向切面编程(AOP)的应用场景下,这样的操作可能会遇到一些意想不到的行为。 对于同一类内的自我调用情况,由于这些调用发生在对象实例本身而非通过代理来完成,因此不会触发任何由Spring AOP定义的通知机制[^3]。这意味着如果尝试在一个事务管理环境下进行此类操作,则可能发现预期中的声明式事务并未生效。 #### 2. 解决方案概述 为了使嵌套调用能够正常工作并遵循所期望的横切关注点逻辑(如事务控制),可以采取以下几种策略之一: - **利用`AopContext.currentProxy()`获取当前代理对象** 这种方式允许开发者显式地访问到被代理后的目标bean实例,并以此为基础来进行进一步的操作。具体来说就是让某个成员函数改为自己持有的proxy对象上调用相应的方法而不是直接调用自己的版本。 ```java @AllArgsConstructor @Service public class SomeServiceImpl implements SomeService { private final SomeService self; @Transactional public void someMethod() { this.self.someInnerMethod(); } public void someInnerMethod(){ // 实际业务逻辑... } } ``` - **重构代码结构** 另一种更为推荐的做法是对现有设计模式做出调整,使得原本位于单个组件内部的功能拆分至不同层次的服务层之间,从而自然形成跨Bean边界的交互路径。这样做不仅有助于提高系统的模块化程度,同时也更容易维护和测试。 #### 3. 关键技术细节说明 - 当使用`@Transactional`注解标记某方法时,它实际上是由Spring容器负责创建的一个动态代理对象来实际执行这个标注了事务特性的过程。但是,一旦进入到了具体的Java类体之后再去做其他非公开接口上的调用就不会再次经过这层额外封装了[^2]。 - 对于那些确实需要保持原有架构不变而又希望保留住必要的环绕通知功能的情形而言,借助`AopContext.currentProxy()`不失为一条可行之路。不过需要注意的是,默认配置下的Spring环境并不会开启这项特性,所以还需要确保已在application.properties文件里设置了相应的参数开关以激活此选项[^5]。 ```properties spring.aop.proxy-target-class=true ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值