前言
这部分内容基于Spring实战第四版,讲述Spring中面向切面编程的使用。
术语介绍
通知:
切面的工作被称为通知,通知定义了切面是什么以及何时使用,某个方法调用之前或调用之后。
Spring切面可以应用5种类型的通知:
前置通知;
后置通知;
返回通知;
异常通知;
环绕通知;
连接点:
连接点事在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时。切面代码可以利用这些点插入到应用的正常流程中,并添加新的行为。
切点:
切点定义了何处插入一个切面(我理解为连接点的子集)。切点的定义会匹配通知所要织入的一个或多个连接点。
切面:
切面是通知和切点的结合。通知和切面定义了切面的全部内容。
引入:
引入允许我们向现有类添加新方法或属性。
织入:
织入是吧切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:
- 编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器,AspectJ的织入编译器就是这种方式
- 类加载期间:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5支持这种方式。
- 运行期:切面在应用程序运行的某个时刻织入,一般情况下,在织入切面时,AOP容器会为目标对象动态的创建一个代理对象SpringAOP就是以这种方式织入切面的。
Spring中的AOP
并不是所有AOP框架都是相同的,它们在连接点模型上可能有强弱之分,有些可以在字段修饰符级别应用通知,而另一些只支持方法调用相关的连接点。它们织入切面的方式和时机也有所不同,但无论如何,创建切点来定义切面所织入的连接点是AOP框架的基本功能。
Spring提供了四种AOP支持:
- 基于代理的经典Spring’AOP
- 纯POJO切面
- @AspectJ注解驱动的切面
- 注入式AspectJ切面
前三种都是SpringAOP实现的变体,SpringAOP构建在动态代理的基础之上。
*注:Spring只支持方法级别的连接点。
Spring借鉴了AspectJ。下面是SpringAOP所支持的AspectJ切点指示器。
同时,Spring还引入了一个新的Bean()指示器,它允许我们在切点表达式中使用Bean的ID来标识Bean。
定义切点表达式
开启自动代理
如果使用XML的Bean配置方式,那么需要使用Spring aop命名空间中的元素,这样你才能使用@AspectJ注解
<!--头需要添加Spring aop的命名空间-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/SMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:somponent-scan base-package="concert" />
<!--启用AspectJ代理-->
<aop:aspectj-autoproxy />
<bean class="concert.Aaudience" />
>
</beans>
使用注解定义切面
假设我们已经定义了一个Performance类,它有个perform方法
@AspectJ
public class Audience {
@Before("execution(** concert.Performance.perform(..))")
public void silenceCellPhones(){
System.out.println("silencing cell phones");
}
}
Audience类使用@AspectJ注解标注,声明它为一个切面。
进一步使用注解可为Audience定义切面的行为。
- @before 通知方法会在目标方法调用前执行
- @After 通知方法会在目标方法返回或抛出异常后执行
- @AfterReturning 通知方法会在目标方法返回后执行
- @AfterThrowing 通知方法会在目标方法抛出异常后执行
- @Around 使用比较特殊
@Pointcut定义切点
//在标注了@AspectJ类中使用下面代码,可以定义一个可重用的切点
@Pointcut("wxwcution(** concert.Performance.perform(..))")
public void performance(){}//这个方法内部不需要代码,实际上它只是用来标记切点。
//使用上面定义的切点
@After("performance()")
public void applause(){
...
}
@Around环绕通知
使用这个注解可以将目标方法完全包装起来,使用它需要在通知方法中添加一个ProceedingJoinPoint类型的参数,并在通知方法中执行这个参数的proceed(0方法(类似JVM动态代理的invoke方法)
@Around("performance()")
public void watchPerformance(PeoceedingJoinPoint jp) {
try{
System.out.println("Silencing cell phones");
jp.proceed();//此时执行目标方法
System.out.println("CLAP CLAP CLAP");
} catch (Exception e) {
System.out.println("Demanding a refund");
}
}
准备先研究SpringMVC的原理。。。待续