当在同一个类中,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. js代码突然在花括号回车自动多加了一个大括号

    js代码突然在花括号回车自动多加了一个大括号. 检查发现,前面有一个行带有左括号的注释行.

  2. Visual-Based Autonomous Driving Deployment from a Stochastic and Uncertainty-Aware Perspective

    张宁 Visual-Based Autonomous Driving Deployment from a Stochastic and Uncertainty-Aware Perspective Le ...

  3. git rebase使用场景

    1. 当前分支落后拉取后,整理commit,使得提交历史为直线 git pull = git fetch + git merge git pull --rebase = git fetch + git ...

  4. java js ur特殊格式处理 json 特殊格式处理

    url特殊格式处理: js中使用 encodeURIComponent() 编码对应的value $.ajax({ type: "post", url: "/tb_are ...

  5. idea修改maven项目名

    1.修改pom.xml中相关 <artifactId>seal-hn</artifactId><name>seal-hn</name><descr ...

  6. Java 8并行流的性能陷阱

    并行化流被分成多个块,每个块独立处理,结果在最后汇总. CPU密集型代码如下: private long countPrimes(int max) {     return range(1, max) ...

  7. LeetCode70——爬楼梯

    题目描述 假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数. 示例 1: 输入: 2 输出: 2 解 ...

  8. SGU 126. Boxes --- 模拟

    <传送门> 126. Boxes time limit per test: 0.25 sec. memory limit per test: 4096 KB There are two b ...

  9. 插件部署【BE、BP、UI】

    1.BE插件部署 XML部署位置:D:\yonyou\U9V50\Portal\bin: DLL部署位置:D:\yonyou\U9V50\Portal\ApplicationServer\Libs; ...

  10. Locust性能测试-no-web模式和csv报告保存 转自:悠悠

    前言 前面是在web页面操作,需要手动的点start启动,结束的时候也需要手工去点stop,没法自定义运行时间,这就不太方便. locust提供了命令行运行的方法,不启动web页面也能运行,这就是no ...