Spring in Action --- 第四章 面向切面的Spring
什么是面向切面编程
以马士兵老师最简单的话来说,你有一个查询的请求,查询的过程是一条线,在这条线的头和尾咔嚓切两刀,然后往里面拼入两条新的线,这就是面向切面编程.
具体一点来说,有一些功能是大部分方法都需要的,比如安全性,事务,但是又不希望在许多地方重复的造轮子.你可能会想到用代理,或者将这些方法抽取为一个类后引用,但是仔细想一想,这真的合适吗?比如一个查询的操作,关心的可能只是能不能查到数据,而不会去关心安全性,因为这并不是查询操作所要做的事情.在这种情况下,面向切面编程就能帮上很大的忙
定义AOP术语
在使用面向切面编程(AOP)之前,我们先来了解一下AOP的专有名词
通知
通知定义了且面试什么以及何时使用,Spring 切面可以应用5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能
- 后置通知(After):在目标方完成之后调用通知,不关心方法的输出是什么
- 返回通知(After-Return):在目标方法成功执行后调用通知
- 异常通知(After-Throwing):在目标方法抛出异常后调用通知
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
连接点
连接点是在应用执行过程中能够插入切面的一个点,切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为
切点
如果是通知定义了切面的"什么"和"何时"的话,那么切点就定义了"何处",我们定义在何处需要这些切面代码
切面
切面是切点和通知的结合,切点和通知共同定义了切面的全部内容------它是什么,在何处和何时完成其功能.
引入
引入允许我们向现有的类添加新方法或属性.
织入
织入是把切面应用到目标对象并创建新的代理对象的过程,在目标对象的生命周期里有多个点可以进行织入:
- 编译期 --- 切面在目标类编译时被织入,AspectJ的织入编译器就是一这种方式织入切面的
- 类加载期 --- 切面在目标类加载到JVM时被织入
- 运行期 --- 切面在应用运行时的某个时刻被织入
Spring对AOP的支持
Sping提供了4中类型的AOP支持:
- 基于代理的经典Spring AOP
- 纯POJO切面
- @AspectJ 注解驱动的切面
- 注入式AspectJ切面
Spring AOP构建在动态代理基础之上,因此,Spring 对 AOP的支持局限于方法拦截
Spring通知是Java编写的
Spring所创建的通知全都是用标准的Java类编写的,这样的话,我们可以方便的开发切面,但是AspectJ与之相反,它是通过Java语言扩展的方式实现的,通过特有的AOP语言,我们可以获得更强大和细粒度的控制,但是需要学习额外的工具和语法
Spring在运行时通知对象
通过在代理类中包裹切面,Spring在运行期把切面植入到Spring管理的bean中.直到应用需要被代理的bean时,Spring才创建代理对象
Spring只支持方法级别的连接点
因为Spring基于动态代理,所以只支持方法连接点.
通过切点来选择连接点
在Spring AOP中,要使用 AspectJ的其诶单表达式语言来定义切点.关于Spring AOP的AspectJ切点,最重要的一点就是Spring仅支持AspectJ切点指示器的一个子集,下表列出了Spring AOP所支持的AspectJ切点指示器:
AspectJ 指示器 | 描 述 |
---|---|
arg() | 限制连接点匹配参数为指定类型的执行方法 |
@args() | 限制连接点匹配参数有指定注解标注的执行方法 |
execution() | 用于匹配是连接点的执行方法 |
this() | 限制连接点匹配AOP代理的bean引用为指定类型的类 |
target() | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型(当使用 Spring AOP 时,方法定义在由指定的注解所标注的类里) |
@annotation | 限制匹配带有指定注解的连接点 |
注意以上指示器中只有execution指示器是实际执行匹配的,其他的指示器都是用来限制匹配的
编写切点
下面是一个简单的示例:
execution(* concert.Performance.perform(..)
第一个星号表示返回任意类型,后面表示使用任意参数的指定某个类的某一个方法.如果希望匹配指定包下所有的方法,可以这么写:
execution(* com.**.*(..))
表示com包及其子包下任何类的使用任何参数的任何方法都会被匹配到.
现在我们假设需要配置的切点仅匹配concert包,可以使用within()指示器来限制匹配:
execution(* concert.Performance.perform(..) && within(concert.*)
在切点中选择bean
Spring引入了一个新的指示器:bean(),它允许我们在切点表达式中使用bean的ID来标识bean,bean()使用beanID或bean名称作为参数来限制切点只匹配特定的bean