理解 AOP
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程。
AOP 最早是 AOP 联盟的组织提出的,指定的一套规范,spring 将 AOP 的思想引入框架之中,通过预编译方式和运行期间动态代理实现程序的统一维护的一种技术,
- Spring 框架通过定义切面,通过拦截切点实现了不同业务模块的解耦,这个就叫面向切面编程 - Aspect Oriented Programming (AOP)
- 为什么 @Aspect 注解使用的是 aspectj 的 jar 包呢?这就引出了 Aspect4J 和 Spring AOP 的历史渊源,只有理解了 Aspect4J 和 Spring 的渊源才能理解有些注解上的兼容设计
- 如何支持更多拦截方式来实现解耦, 以满足更多场景需求呢? 这就是 @Around, @Pointcut… 等的设计
- 那么 Spring 框架又是如何实现 AOP 的呢? 这就引入代理技术,分静态代理和动态代理,动态代理又包含 JDK 代理和 CGLIB 代理等
一个例子
将记录日志功能解耦为日志切面,它的目标是解耦。进而引出 AOP 的理念:就是将分散在各个业务逻辑代码中相同的代码通过横向切割的方式抽取到一个独立的模块中!
- OOP 面向对象编程,针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
- AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或阶段,以获得逻辑过程的中各部分之间低耦合的隔离效果。
AOP 术语
- 连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring 只支持方法执行连接点,在 AOP 中表示为在哪里干;
- 切入点(Pointcut): 选择一组相关连接点的模式,即可以认为连接点的集合,Spring 支持 perl5 正则表达式和 AspectJ 切入点模式,Spring 默认使用 AspectJ 语法,在 AOP 中表示为在哪里干的集合;
- 通知(Advice):在连接点上执行的行为,通知提供了在 AOP 中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知 (after advice)、环绕通知(around advice),在 Spring 中通过代理模式实现 AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在 AOP 中表示为干什么;
- 方面 / 切面(Aspect):横切关注点的模块化,比如上边提到的日志组件。可以认为是通知、引入和切入点的组合;在 Spring 中可以使用 Schema 和 @AspectJ 方式进行组织实现;在 AOP 中表示为在哪干和干什么集合;
- 引入(inter-type declaration):也称为内部类型声明,为已有的类添加额外新的字段或方法,Spring 允许引入新的接口(必须对应一个实现)到所有被代理对象(目标对象), 在 AOP 中表示为干什么(引入什么);
- 目标对象(Target Object):需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被通知的对象,从而也可称为被通知对象;由于 Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在 AOP 中表示为对谁干;
- 织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用 AspectJ 编译器),类加载时和运行时完成。Spring 和其他纯 Java AOP 框架一样,在运行时完成织入。在 AOP 中表示为怎么实现的;
AOP 代理(AOP Proxy):AOP 框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在 Spring 中,AOP 代理可以用 JDK 动态代理或 CGLIB 代理实现,而通过拦截器模型应用切面。在 AOP 中表示为怎么实现的一种典型方式;
通知类型
前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
- 后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
- 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
- 最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
- 环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
环绕通知是最常用的通知类型。和 AspectJ 一样,Spring 提供所有类型的通知,我们推荐你使用尽可能简单的通知类型来实现需要的功能。
例如,如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成同样的事情。
用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。比如,你不需要在 JoinPoint 上调用用于环绕通知的 proceed () 方法,就不会有调用的问题。