当在同一个类中,A方法调用B方法时,AOP无法工作的问题

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

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; @Aspect
public class CustomerServiceInterceptor { @Before("execution(* demo.long..*.*(..))")
public void doBefore() {
System.out.println("do some important things before...");
}
}

把Bean加到Spring配置中

<aop:aspectj-autoproxy />

<bean id="customerService" class="demo.long.impl.CustomerServiceImpl" />
<bean id="customerServiceInterceptor" class="demo.long.CustomerServiceInterceptor" />
 

如果现在外部对象调用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配置:
      <aop:aspectj-autoproxy expose-proxy="true" />

转自: https://www.jianshu.com/p/6534945eb3b5

Spring AOP无法拦截内部方法调用的更多相关文章

  1. spring aop获取目标对象的方法对象(包括方法上的注解)

    这两天在学习权限控制模块.以前看过传智播客黎活明老师的巴巴运动网视频教程,里面就讲到权限控制的解决方案,当时也只是看看视频,没有动手实践,虽说看过几遍,可是对于系统中的权限控制还是很迷茫,所以借着这次 ...

  2. Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)

    Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效) 前言 今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题, ...

  3. Spring service本类中方法调用另一个方法事务不生效问题(转载)

    前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...

  4. Spring AOP不拦截从对象内部调用的方法原因

    拦截器的实现原理很简单,就是动态代理,实现AOP机制.当外部调用被拦截bean的拦截方法时,可以选择在拦截之前或者之后等条件执行拦截方法之外的逻辑,比如特殊权限验证,参数修正等操作. 但是最近在项目中 ...

  5. Spring 内部方法调用失效问题(AOP)

    AOP使用的是动态代理的机制,它会给类生成一个代理类,事务的相关操作都在代理类上完成.内部方式使用this调用方式时,使用的是实例调用,并没有通过代理类调用方法,所以会导致事务失效. 解决办法 方式一 ...

  6. AOP方法增强自身内部方法调用无效 SpringCache 例子

    开启注解@EnableCaChing,配置CacheManager,结合注解@Cacheable,@CacheEvit,@CachePut对数据进行缓存操作 缺点:内部调用,非Public方法上使用注 ...

  7. Spring AOP无法拦截Controller中的方法

    想使用AOP Annotation配置Spring MVC的Controller进行拦截, 发现无法拦截Controller的方法, 却可以拦截Service层的方法. 一开始: Spring的配置文 ...

  8. Spring Aop、拦截器、过滤器的区别

    Filter过滤器:拦截web访问url地址.Interceptor拦截器:拦截以 .action结尾的url,拦截Action的访问.Spring AOP拦截器:只能拦截Spring管理Bean的访 ...

  9. spring---aop(3)---Spring AOP的拦截器链

    写在前面 时间断断续续,这次写一点关于spring aop拦截器链的记载.至于如何获取spring的拦截器,前一篇博客已经写的很清楚(spring---aop(2)---Spring AOP的JDK动 ...

随机推荐

  1. requests库学习案例

    requests库使用流程 使用流程/编码流程 1.指定url 2.基于requests模块发起请求 3.获取响应对象中的数据值 4.持久化存储 分析案例 需求:爬取搜狗首页的页面数据 # 爬取搜狗首 ...

  2. 【tensorflow-v2.0】如何查看模型的输入输出流的属性

    操作过程: 1. 查看mobilenet的variables loaded = tf.saved_model.load('mobilenet') print('MobileNet has {} tra ...

  3. javascript下載csv檔案

    參考自: https://dotblogs.com.tw/shihgogo/2017/05/31/090831 function createCsvFile(){ var fileName = &qu ...

  4. Xcode7.2真机测试问题"The account 'appleID ' has no team with ID ‘’

     在Xcode7(测试版)提出免费真机测试的时候,我立刻在网上搜寻测试步骤,很简单,按照步骤走就可以. 但在7.2以后,突然我的iPhone不能真调了!提示"The account 'app ...

  5. [UE4] TSharedPtr, TWeakObjectPtr and TUniquePtr

    转自:https://dawnarc.com/2018/07/ue4-tsharedptr-tweakobjectptr-and-tuniqueptr/ UE4 的 TSharedPtr.TWeakO ...

  6. distinct 数组去重,对象去重

    distinct 操作符可以用来去重,将上游重复的数据过滤掉. import { of } from 'rxjs'; import { distinct} from 'rxjs/operators'; ...

  7. proc 下创建与应用交互的可读写节点

    内核版本:Linux-4.14 随便写了个 proc 下节点的测试程序,可以用来与应用层交互. 也可以单独的用来做调试打印使用,例如封装个 my_printk 将信息单独存在节点内,然后可以在应用层 ...

  8. Ubuntu下好用的pdf工具

    安装okular sudo apt-get install okular 汉化 sudo apt-get install kde-l10n-zhcn 然后打开PDF文件时,右键选择打开方式选择okul ...

  9. LeetCode 942. 增减字符串匹配(DI String Match) 49

    942. 增减字符串匹配 942. DI String Match 题目描述 每日一算法2019/6/21Day 49LeetCode942. DI String Match Java 实现 and ...

  10. [转帖]从0开始的高并发(一)--- Zookeeper的基础概念

    从0开始的高并发(一)--- Zookeeper的基础概念 https://juejin.im/post/5d0bd358e51d45105e0212db 前言 前面几篇以spring作为主题也是有些 ...