当在同一个类中,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 arry: var arry = []; js object: var obj = {}; obj定义属性: obj.filename=''; obj.id=''; 把 obj 添加到 arry ...

  2. Android Studio 3.0——unable to resolve dependency for cordovalib

    Android Studio 3.0 更新了gradle后,项目竟然开始报错unable to resolve dependency for cordovalib...打开build.gradle看了 ...

  3. Ubuntu部署Docker容器环境

    1.首先切换到root用户 2.安装网卡报错 解决办法,删除锁住的文件: 再次安装成功. 4.ubuntu下面安装:apt-get install openssh-server  安装远程工具 5.设 ...

  4. 【SQL骚操作】SqlServer数据库表生成C# Model实体类SQL语句

    已知现有表T1 想快速获取cs类结构 /// <summary> /// T1 /// </summary> public class T1 { /// <summary ...

  5. JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.

    JobStorage.Current property value has not been initialized. You must set it before using Hangfire Cl ...

  6. Java的集合类之Set接口

    Set最大的特性就是不允许在其中存放的元素是重复的.根据这个特点,我们就可以使用Set 这个接口来实现前面提到的关于商品种类的存储需求.Set 可以被用来过滤在其他集合中存放的元素,从而得到一个没有包 ...

  7. intellij idea设置代码提示不区分大小写

    https://blog.csdn.net/csm0401/article/details/86306417

  8. window 关机

    schtasks /create /tn "关机" /tr "shutdown /s" /sc once /st 20:30

  9. MyBatis 学习笔记(七)批量插入ExecutorType.BATCH效率对比

    MyBatis 学习笔记(七)批量插入ExecutorType.BATCH效率对比一.在mybatis中ExecutorType的使用1.Mybatis内置的ExecutorType有3种,默认的是s ...

  10. Codeforces Round #603 (Div. 2) (题解)

    A. Sweet Problem (找规律) 题目链接 大致思路: 有一点瞎猜的,首先排一个序, \(a_1>a_2>a_3\) ,发现如果 \(a_1>=a_2+a_3\) ,那么 ...