博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring AOP无法拦截内部方法调用-- expose-proxy="true"用法
阅读量:4631 次
发布时间:2019-06-09

本文共 3255 字,大约阅读时间需要 10 分钟。

假设一个接口里面有两个方法:

package demo.long;public interface CustomerService {      public void doSomething1();      public void doSomething2();  }

接口实现类如下:

package demo.long.impl;import demo.long.CustomerService; public class CustomerServiceImpl implements CustomerService {        public void doSomething1() {          System.out.println("CustomerServiceImpl.doSomething1()");          doSomething2();      }        public void doSomething2() {          System.out.println("CustomerServiceImpl.doSomething2()");      }    }

现在我需要在CustomerService接口的每个方法被调用时都在方法前执行一些逻辑,所以需要配置一个拦截器:

package demo.long;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class CustomerServiceInterceptor {    @Before("execution(* demo.long..*.*(..))")    public void doBefore() {        System.out.println("do some important things before...");     }}

把Bean加到Spring配置中

如果现在外部对象调用CustomerService的doSomething1()方法的时候,会发现只有doSomething1()方法执行前打印了“do some important things before...”,而doSomething1()内部调用doSomething2()时并没有打印上述内容;外部对象单独调用doSomething2()时会打印上述内容。

public class CustomerServiceTest {    @Autowired    ICustomerService customerService;    @Test    public void testAOP() {        customerService.doSomething1();    }}

原因分析

拦截器的实现原理就是动态代理,实现AOP机制。Spring 的代理实现有两种:一是基于 JDK Dynamic Proxy 技术而实现的;二是基于 CGLIB 技术而实现的。如果目标对象实现了接口,在默认情况下Spring会采用JDK的动态代理实现AOP,CustomerServerImpl正是这种情况。

JDK动态代理生成的CustomerServiceImpl的代理类大致如下:

public class CustomerServiceProxy implements CustomerService {        private CustomerService customerService;        public void setCustomerService(CustomerService customerService) {          this.customerService = customerService;      }        public void doSomething1() {          doBefore();          customerService.doSomething1();      }        public void doSomething2() {          doBefore();          customerService.doSomething2();      }        private void doBefore() {          // 例如,可以在此处开启事务或记录日志        System.out.println("do some important things before...");      }    }

客户端程序使用代理类对象去调用业务逻辑:

public class TestProxy {            public static void main(String[] args) {          // 创建代理目标对象        // 对于Spring来说,这一工作是由Spring容器完成的。          CustomerService serviceProxyTarget = new CustomerServiceImpl();            // 创建代理对象        // 对于Spring来说,这一工作也是由Spring容器完成的。         CustomerServiceProxy serviceProxy = new CustomerServiceProxy();          serviceProxy.setCustomerService(serviceProxyTarget);          CustomerService serviceBean = (CustomerService) serviceProxy;            // 调用业务逻辑操作          serviceBean.doSomething1();      }  }

执行main方法,发现doSomething1()中调用doSomething2()方法的时候并未去执行CustomerServiceProxy类的doBefore()方法。其实doSomething2()等同于this.doSomething2(),在CustomerServiceImpl类中this关键字表示的是当前这个CustomerServiceImpl类的实例,所以程序会去执行CustomerServiceImpl对象中的doSomething2()方法,而不会去执行CustomerServiceProxy类对象中的 doSomething2()方法。

在使用Spring AOP的时候,我们从IOC容器中获取的Bean对象其实都是代理对象,而不是那些Bean对象本身,由于this关键字引用的并不是该Service Bean对象的代理对象,而是其本身,因此Spring AOP是不能拦截到这些被嵌套调用的方法的。

解决方案

  1. 修改类,不要出现“自调用”的情况:这是Spring文档中推荐的“最佳”方案;
  2. 若一定要使用“自调用”,那么this.doSomething2()替换为:((CustomerService) AopContext.currentProxy()).doSomething2();此时需要修改spring的aop配置:

 转自:

转载于:https://www.cnblogs.com/ithfm/p/10281355.html

你可能感兴趣的文章
ubuntu下如何用命令行运行deb安装包
查看>>
luacom cygwin
查看>>
『ACM C++』 PTA 天梯赛练习集L1 | 044-45
查看>>
Duilib
查看>>
hdu--2545--数据弱了?
查看>>
链接思维导图 -读《深入理解计算机系统》
查看>>
PostgreSQL窗口函数
查看>>
学习笔记(一)
查看>>
android一体机的开发问题
查看>>
java中equals和==的区别
查看>>
bzoj3675: [Apio2014]序列分割
查看>>
LC-349 两个数组的交集
查看>>
POJ 1087 A Plug for UNIX 【最大流】
查看>>
2013人人网校园招聘笔试题
查看>>
JavaScript:概述
查看>>
验证视图状态MAC失败解决方案
查看>>
RCP学习笔记
查看>>
CentOS双网卡双IP设置
查看>>
敏捷软件开发:原则、模式与实践——第2章 极限编程概述
查看>>
sql优化------查询整个表按照某个字段排序后的前几条
查看>>