Spring AOP无法拦截内部方法调用
当在同一个类中,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是不能拦截到这些被嵌套调用的方法的。
解决方案
- 修改类,不要出现“自调用”的情况:这是Spring文档中推荐的“最佳”方案;
- 若一定要使用“自调用”,那么this.doSomething2()替换为:((CustomerService) AopContext.currentProxy()).doSomething2();此时需要修改spring的aop配置:
<aop:aspectj-autoproxy expose-proxy="true" />
转自: https://www.jianshu.com/p/6534945eb3b5
Spring AOP无法拦截内部方法调用的更多相关文章
- spring aop获取目标对象的方法对象(包括方法上的注解)
这两天在学习权限控制模块.以前看过传智播客黎活明老师的巴巴运动网视频教程,里面就讲到权限控制的解决方案,当时也只是看看视频,没有动手实践,虽说看过几遍,可是对于系统中的权限控制还是很迷茫,所以借着这次 ...
- Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)
Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效) 前言 今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题, ...
- Spring service本类中方法调用另一个方法事务不生效问题(转载)
前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...
- Spring AOP不拦截从对象内部调用的方法原因
拦截器的实现原理很简单,就是动态代理,实现AOP机制.当外部调用被拦截bean的拦截方法时,可以选择在拦截之前或者之后等条件执行拦截方法之外的逻辑,比如特殊权限验证,参数修正等操作. 但是最近在项目中 ...
- Spring 内部方法调用失效问题(AOP)
AOP使用的是动态代理的机制,它会给类生成一个代理类,事务的相关操作都在代理类上完成.内部方式使用this调用方式时,使用的是实例调用,并没有通过代理类调用方法,所以会导致事务失效. 解决办法 方式一 ...
- AOP方法增强自身内部方法调用无效 SpringCache 例子
开启注解@EnableCaChing,配置CacheManager,结合注解@Cacheable,@CacheEvit,@CachePut对数据进行缓存操作 缺点:内部调用,非Public方法上使用注 ...
- Spring AOP无法拦截Controller中的方法
想使用AOP Annotation配置Spring MVC的Controller进行拦截, 发现无法拦截Controller的方法, 却可以拦截Service层的方法. 一开始: Spring的配置文 ...
- Spring Aop、拦截器、过滤器的区别
Filter过滤器:拦截web访问url地址.Interceptor拦截器:拦截以 .action结尾的url,拦截Action的访问.Spring AOP拦截器:只能拦截Spring管理Bean的访 ...
- spring---aop(3)---Spring AOP的拦截器链
写在前面 时间断断续续,这次写一点关于spring aop拦截器链的记载.至于如何获取spring的拦截器,前一篇博客已经写的很清楚(spring---aop(2)---Spring AOP的JDK动 ...
随机推荐
- k8s记录-国内下载k8s组件镜像
#!/bin/sh ### 版本信息 K8S_VERSION=v1.13.2 ETCD_VERSION=3.2.24 DASHBOARD_VERSION=v1.8.3 FLANNEL_VERSION= ...
- dotnet core 3.0 swagger 显示枚举描述
上一篇net core 2.2 swagger的枚举描述,core 3.0 需要升级swagger到5.0rc版,配置需要做些修改,swaager启用了OpenApi标准,之前的枚举描述方法也失效了. ...
- 【Docker学习之二】Docker部署安装
环境 docker-ce-19.03.1-3.el7.x86_64 一.Docker的部署安装 Docker采用Linux(内核)技术,所以只能运行在Linux上,官方说Linux kernel至少3 ...
- mysql新建用户host使用%但使用localhost无法连接
今天新建了一个用户,权限也给了,host设置的为% 但是使用该用户连接时出现如下错误 查看用户 mysql> select host,user,password from mysql.user; ...
- 以Unicode(UTF-16 LE)编码保存文本
1. 以二进制方式打开文件,写入BOM头 FILE* pFile = nullptr; _wfopen_s(&pFile, szLogFilePath, L"wb"); / ...
- java网站架构演变过程
网站架构演变过程. .传统架构.传统的SSH架构,分为三层架构web控制层.业务逻辑层.数据库访问层..传统架构也就是单点应用,就是大家在刚开始初学JavaEE技术的时候SSH架构或者SSM架构,业务 ...
- 【读书笔记】胡说IC
- python之数据解构和算法进阶
1.解压赋值多个变量 采用解构的方法.可迭代对象才可以,变量数量与元素个数要一一对应,或者采用*万能接收. 2.解压可迭代对象赋值多个变量 如果一个可迭代对象的元素个数超过变量个数时,会抛出一个 Va ...
- 2、在NET中实现多线程
1.System.Threading命名空间 System.Threading命名空间提供了使得可以多线程编程的类和接口 其中 (1)Thread类构成了C#多线程编程的支柱,他用于创建并控制线程 ( ...
- 如何让 height:100%; 起作用---父级元素必须设定高度
参考: http://www.webhek.com/post/css-100-percent-height.html https://www.cnblogs.com/kunmomo/p/1060066 ...