AOP使用小记
调用this使aop失效
曾在工作中遇到过自定义了一个@logable的日志注解,对manager层进行拦截记录日志,其中有一个方法中调用了内部的一个方法,该方法本身也有修改操作,用作复用。
结果日志中始终没有打印调用方法的日志。
好奇之下进行研究,发现aop无法拦截住内部方法的调用原因可见此demo, 理解了原因,我当时是直接注入了自身服务进行调用,发现日志就可以拦截到了。
希望可以帮助到有此困惑的小伙伴。我会陆续将以前记录的东西持续分享出来。
记录的一个demo
这是记录的一个比较简单的demo,可以初步了解到aop拦截的原理以及验证了我工作用使用aop注解失效的问题
测试类
package com.sunyard.unified.csrt.spring.aop.test;
import com.sunyard.unified.csrt.spring.aop.ICustomerService;
import com.sunyard.unified.csrt.spring.aop.impl.CustomerServiceImpl;
import com.sunyard.unified.csrt.spring.aop.proxy.CustomerServiceProxy;
/**
* @author chaotan.liu
* @version 1.0
* @date 2020/7/24 8:51
* 在开发基于 Spring 的应用的过程中碰到了一个让我困惑了好久的问题,我在一个 Service 类的 doSomething1() 方法中通过
* this.doSomething2(); 语句调用了同一个类中的 doSomething2 方法,运行时通过调试发现 doSomething1 方法的执行前
* 后正常地执行了自定义的 around 装备,但是在 doSomething2 方法执行前后并未如我所期望的那样执行自定义的 around advice 。
* 今天终于恍然大悟,把它当作笔记写下来。Spring 的代理实现有两种:一是基于 JDK Dynamic Proxy 技术而实现的;二是基于 CGLIB
* 技术而实现的。今天的目标是探索基于 JDK Dynamic Proxy 的动态代理。首先来看看如何自己动手实现一个对 Service Bean 对象的动态代理。
* 为了能够更清楚地看到 Spring AOP 动态代理的本质,我决定不使用 JDK 中提供的 Dynamic Proxy API,就使用最普通的 java 代码来
* 模拟一个动态代理实例。
*/
public class TestProxy {
public static void main(String[] args) {
// 创建代理目标对象。对于Spring来说,这一工作
// 是由Spring DI容器完成的。
ICustomerService serviceProxyTarget = new CustomerServiceImpl();
// 创建代理对象。对于Spring来说,这一工作
// 也是由Spring DI容器完成的。
CustomerServiceProxy serviceProxy = new CustomerServiceProxy();
serviceProxy.setCustomerService(serviceProxyTarget);
ICustomerService serviceBean = (ICustomerService) serviceProxy;
// 调用业务逻辑操作
serviceBean.doSomething1();
}
/**
* 现在以调试方式运行这个应用,你会发现在 doSomething1() 中调用 doSomething2() 方法的
* 时候并未去执行CustomerServiceProxy 类的 doBefore()、doAfter() 方法。再来看看这句
* 关键代码:doSomething2(); 把它隐含的意思也表达出来吧:this.doSomething2(); 哦,我
* 明白了,在 CustomerServiceImpl 类中 this 关键字表示的是当前这个CustomerServiceImpl
* 类的实例。那程序当然就会去执行 CustomerServiceImpl 类中的 doSomething2() 方法了,而不
* 会去执行 CustomerServiceProxy 类中的 doSomething2() 方法!!
* 在使用 Spring AOP 的时候,我们从 IOC 容器中获取的 Service Bean 对象其实都是代理对象,
* 而不是那些 Service Bean 对象本身,也就是说获取的并不是被代理对象或代理目标。当我在自己的
* Service 类中使用 this 关键字嵌套调用同类中的其他方法时,由于 this 关键字引用的并不是该
* Service Bean 对象的代理对象,而是其本身,故 Spring AOP 是不能拦截到这些被嵌套调用的方法的。
*/
}
业务接口
package com.sunyard.unified.csrt.spring.aop;
public interface ICustomerService {
public void doSomething1();
public void doSomething2();
}
业务实现
package com.sunyard.unified.csrt.spring.aop.impl;
import com.sunyard.unified.csrt.spring.aop.ICustomerService;
public class CustomerServiceImpl implements ICustomerService {
public void doSomething1() {
System.out.println("Inside CustomerServiceImpl.doSomething1()");
this.doSomething2();
new CustomerServiceImpl().doSomething2();
}
public void doSomething2() {
System.out.println("Inside CustomerServiceImpl.doSomething2()");
}
}
代理类实现
package com.sunyard.unified.csrt.spring.aop.proxy;
import com.sunyard.unified.csrt.spring.aop.ICustomerService;
public class CustomerServiceProxy implements ICustomerService {
private ICustomerService customerService;
public void setCustomerService(ICustomerService customerService) {
this.customerService = customerService;
}
public void doSomething1() {
doBefore();
customerService.doSomething1();
doAfter();
}
public void doSomething2() {
doBefore();
customerService.doSomething2();
doAfter();
}
private void doBefore() {
// 例如,可以在此处开启事务
System.out.println("方法执行前.");
}
private void doAfter() {
// 例如,可以在此处提交或回滚事务、释放资源等等
System.out.println("方法执行后...");
}
}