文章目录
前言
前面聊了Bean初始化的整体流程,总体来说还是在IOC上。今天想聊聊AOP,希望能给你一个回答Spring AOP相关问题的逻辑结构。
一、AOP是什么?
AOP全称 Aspect Oriented Programming,中文翻译面向切面编程。类似的有OOP,Object Oriented Programming,面向对象编程,想必你非常熟悉。OOP中有一句话叫“万物皆对象”,意味对象(Object)是一等公民。显然,如果你在AOP,那么你就始终围绕Aspect 展开。
二、解决什么问题
经常听到的场景是,统一性能监控,统一日志输出,统一异常捕捉。这些场景的共性是,需要将一段代码重复插入到各种代码块中,并且还与业务逻辑本身关系不大。和通常的业务逻辑自上而下的纵向分布相比,重复的代码插入更像是一种横向分布。AOP就如何以便于人工维护的方式将横向逻辑和纵向逻辑结合问题给出了解决方案。
三、基本概念
为了方便你理解,这里举一个场景,案板上放了3根黄瓜,你拿了把菜刀来切。
名字 | 含义 | 举例 |
---|---|---|
PointCut | 切面 | 三根黄瓜一刀切下刀时的那个点,是个统称,此处包含3根黄瓜切入点 |
JoinPoint | 连接点 | 具体某根黄瓜的切入点,重点在具体 |
Advice | 植入逻辑 | 刀选好切入点切下去,此处的“切下去”就是植入逻辑 |
Advisor | 植入器 | 整个场景的上帝,把刀,黄瓜,案板、力量等要素组合在一起,并精准用力 |
四、跟AspectJ的异同
开始之前,咱们基于Java编程,首先是OOP的,因此是基于OOP的代码组织,实现AOP的逻辑。OOP中的一大原则是面向接口,于是AOP中就被拆分为Aspect的定义和实现。
1、切面定义
Spring作为一个框架,不能和某种具体的实现方案绑定,这样不够面向对象。但是作为一种现实存在的方案,AspectJ已经做了比较好的抽象。所以,Spring完全借鉴了其中的概念,进而定义出Spring AOP的顶级接口,兼容AspectJ的切面定义。
2、切面实现
虽然叫切面实现,但实际上是植入实现,毕竟只有把切面植入到目标的位置上才能产生效果。这里从植入时机维度做分类讨论。
编译时植入(Compile Time Weaver)
在业务代码编译完成后,通过特定的编译器,将切面植入到目标位置。因此,编译时植入依赖特定的编译器。
加载时植入(Load Time Weaver)
通过JVM agent机制在加载class文件到JVM前对class文件做响应的转换。
运行时植入(Runtime Weaver)
在类创建时检查确认是否需要植入,如果需要则生成对应的代理对象,在代理对象的增强逻辑中完成植入。
AspectJ框架支持编译时植入和加载时植入。Spring AOP支持加载时植入和运行时植入。对于加载时植入,Spring借助AspectJ来做。毕竟,AspectJ 1.1.0 版本发布于 2003.5.6,Spring 1.0版本发布于2004.03.24,因此AspectJ先于Spring发布。框架设计者在设计Spring时,想必参考了AspectJ并对其中的问题做了改进。
此外,Spring AOP仅支持方法拦截,不支持对属性的拦截,并不是一个完整AOP框架,其设计初衷是对IOC容器的功能增强。
五、Spring AOP实现
1、底层原理
动态代理,Dynamic Proxy,具体的实现有2种,基于JDK接口和基于CGLIB字节码增强库。
- JDK接口
a. 定义业务接口 Handler
public interface Handler {
public void handle();
}
b. 定义业务接口实现类 HandlerImpl
public class HandlerImpl implements Handler{
@Override
public void handle() {
System.out.println("handle");
}
}
c. 定义代理增强类 BeforeAfterTool
public class BeforeAfterTool {
public void before() {
System.out.println("before");
}
public void after() {
System.out.println("after");
}
}
d. 定义InvocationHandler实现类 ProxyHandler
public class ProxyHandler implements InvocationHandler {
private BeforeAfterTool tool;
private Handler handler;
public ProxyHandler(BeforeAfterTool tool, Handler handler) {
this.tool = tool;
this.handler = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
tool.before();
Object result = method.invoke(handler, args);
tool.after();
return result;
}
}
e. 测试验证
public class Test {
@Test
public void runProxy() {
Handler handler = new HandlerImpl();
InvocationHandler proxyHandler = new ProxyHandler(new BeforeAfterTool(), handler);
Handler proxy = (Handler) Proxy.newProxyInstance(Handler.class.getClassLoader(),new Class[]{Handler.class}, proxyHandler);
proxy.handle();
}
}
- CGLIB
a. 基于Enhancer,字节码修改工具在内存中创建子类,需要字节码改写器ASM工具包实现;
b. 通通过Interceptor拦截对代理类的方法调用;
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(HandlerImpl.class.getClassLoader());
enhancer.setSuperclass(HandlerImpl.class);
// 构造callback, 内部基于Interceptor完成。
enhancer.setCallbacks(callbacks);
// 完成代理对象增强
HandlerImpl handler = ehhancer.create();
- 两者的区别
a. JDK 8以后JDK实现和Cglib性能相当。但Spring默认使用JDK实现;
b. 由于JDK的Proxy方案需要目标类有实现接口,因为对于无接口类只能通过Cglib。从另外一个角度,调用的一定得是接口方法,或者说代码是比较OO的;
c. Cglib需要创建子类,对于final类,和final方法无法代理;
2、与Bean初始化衔接
- 通过扩展BeanPostProcessor的实现类 SmartInstantiationAwareBeanPostProcessor;
- 在方法 postProcessBeforeInitialization,生成代理类进而阻断Spring正常的对象创建;
3、应用场景
spring-transaction、spring-cache、spring-async等其中基于注解的方法功能增强;
总结
以上就是今天要讲的内容,本文介绍Spring AOP的实现,希望对你有所帮助。