Spring Aop嵌套调用时没有生效

本文探讨了Spring AOP在同一个类内部方法调用时的局限性,即无法拦截自身方法调用。通过分析JDKDynamicProxy的工作原理,解释了为什么在Service类中直接调用同类方法时,AOP无法生效,并提供了相应的解决方案。

转载自:https://blog.youkuaiyun.com/hong10086/article/details/78424481

  Spring AOP在同一个类里自身方法相互调用时是无法拦截的。比如在一个 Service 类的 doSomething1() 方法中通过doSomething2(); 语句调用了同一个类中的 doSomething2 方法,运行时通过调试发现 doSomething1 方法的执行前后正常地执行了自定义的 around advice,但是在 doSomething2 方法执行前后并未如期望的那样执行自定义的 around advice。原因呢?
  Spring的代理实现有两种:一是基于 JDK Dynamic Proxy 技术而实现的;二是基于CGLIB 技术而实现的。来基于JDK Dynamic Proxy 的动态代理看下Spring AOP怎么弄的。

代码如下:

public interface ICustomerService {  
    public void doSomething1();  
    public void doSomething2();  
}  
 public class CustomerServiceImpl implements ICustomerService {  
  
    public void doSomething1() {  
        System.out.println("Inside CustomerServiceImpl.doSomething1()");  
        doSomething2();  
    }  
  
    public void doSomething2() {  
        System.out.println("Inside CustomerServiceImpl.doSomething2()");  
    }  
}  

  下面来模拟动态生成代理类的过程,若使用 JDK Dynamic Proxy,这一过程是在运行时进行的。CustomerServiceImpl 类对象的代理类:

 public class CustomerServiceProxy implements ICustomerService {  
  
    private ICustomerService customerService;  
  
    public void setCustomerService(ICustomerService customerService) {  
        this.customerService = customerService;  
    }  
  
    public void doSomething1() {  
        doBefore();  
        customerService.doSomething1();  
        doAfter();  
    }  
  
    public void doSomething2() {  
        doBefore();  
        customerService.doSomething2();  
        doAfter();  
    }  
  
    private void doBefore() {  
        // 例如,可以在此处开启事务  
        System.out.println("do some important things before...");  
    }  
  
    private void doAfter() {  
        // 例如,可以在此处提交或回滚事务、释放资源等等  
        System.out.println("do some important things after...");  
    }  
}  

  Spring实际上是在容器中生成了CustomerServiceProxy,将原始的CustomerServiceImpl设置到了CustomerServiceProxy中。
  doSomething1()中调用doSomething2()方法,它隐含的意思是this.doSomething2(),在CustomerServiceImpl类中 this关键字表示的是当前这个CustomerServiceImpl类的实例。那程序会去执行 CustomerServiceImpl 类中的 doSomething2() 方法了,而不会去执行 CustomerServiceProxy 类中的 doSomething2()方法。
  在使用 Spring AOP 的时候,从 IOC 容器中获取的 Service Bean 对象其实都是代理对象,而不是那些 Service Bean 对象本身,也就是说获取的并不是被代理对象或代理目标。当在Service 类中使用 this 关键字嵌套调用同类中的其他方法时,由于 this 关键字引用的并不是该 Service Bean 对象的代理对象,而是其本身,故 Spring AOP 是不能拦截到这些被嵌套调用的方法的。

  怎么解呢?参考 https://blog.youkuaiyun.com/huangjinlong77/article/details/42707571

### 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 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值