上一章基本完成了Spring依赖注入的相关学习,本章进入面向切面编程的学习。本文先对面向切面编程的思想进行记录,下一阶段进行深入的学习。
本章内容
- 面向切面编程的基本原理
- 通过POJO创建切面
- 使用@AspectJ注解
- 为AspectJ切面注入依赖
引言——在软件开发中,散布于应用中多处的功能被称为横切关注点(cross-cutting concern),横切关注点应该是与业务逻辑相分离的,实现这种分离就是面向切面编程(AOP)要解决的问题。
1,什么是AOP
切面的常用术语有通知(advice)、切点(pointcut)和连接点(joint point)。如图所示:
1.1 通知
切面的工作被称为通知。通知定义了切面是什么以及何时使用。Spring切面可以应用5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用再通知,此时不关心方法的输出是什么;
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
1.2 连接点
连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
1.3 切点
一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知的连接点的范围。
如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了何处
1.4 切面
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能
1.5 引入
引入允许我们向现有的类添加新方法或属性。
1.6 织入
织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多个点可以进行织入:
- 编译器:切面在目标类编译时被织入。这种方式需要特殊的编译器
- 类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。
- 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。
Spring提供了4中类型的AOP支持:
- 基于代理的经典Spring AOP;
- 纯POJO切面;
- @AspectJ注解驱动的切面;
- 注入式AspectJ切面(适用于Spring各版本)。
Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。
在基于XML的配置中,可以使用Spring的aop命名空间将纯POJO转换为切面,这些POJO只是提供了满足切点条件时所要调用的方法。
Spring借鉴了AspectJ的切面,以提供注解驱动的AOP。这种AOP风格的好处是能够不使用XML来配置。
Spring中的AOP
Spring在运行时通知对象。也就是说Spring不会在编译器或者类加载期完成织入。通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。代理类会封装目标类,并且拦截被通知党法的调用。
Spring只支持方法级别的连接点。
2,通过切点来选择连接点
在Spring AOP中,使用AspectJ的切点表达式语言来定义切点。Spring只支持AspectJ切点指示器(pointcut designator)的一个子集,这里要深入理解需要参考AspectJ的相关资料。在Spring支持的指示器中,只有execution指示器是实际执行匹配的,而其他的指示器都是用来限制匹配的。
2.1,编写切点
先定义一个接口:
package concert;
public interface Performance{
public void perform();
}
如图所示,使用execution()指示器选择Performance的perform()方法。方法表达式以“*”开始,表明可以返回任意类型。类名需要使用全限定类型,方法的参数列表使用两个点号表明切点要选择任意的perform()方法,因为可能存在多个重载的perform()方法。
如图所示为使用within()指示器的样例。
Spring引入了一个新的bean()指示器,它允许我们在切点表达式中使用bean的ID来标识bean。bean()使用bean ID或bean名称作为参数来限制切点只匹配特定的bean。例如:
execution(* concert.Performance.perform()) and bean('woodstock')