Spring in Action 4 读书笔记之使用标签创建 AOP

字数1,311 大约花费6分钟

目录

  1. 1. 定义一个 aspect
  2. 2. 创建一个 around advice

在之前的读书笔记 Spring in Acton 4 读书笔记之 AOP 原理及 Spring 对 AOP 的支持 中,讲到 Spring 对 AOP 的支持包含四方面:

  1. Spring 基于代理的经典的 AOP
  2. 使用 XML 配置将纯 POJO 转化为 aspect
  3. 使用 Spring 的 @Aspect 标签,创建 aspect
  4. Spring 不创建 aspect,只注入(引用)AspectJ 框架的 aspect

Spring in Action(Spring 实战)的第四章第三节(4.3 Creating annotated aspects)讲述了其中第三种,即如何使用标签创建 aspect。本文讲解其中的前面两小节:定义 aspect 以及创建 around advice。

AspectJ 5 引进的主要特性是使用标签创建 aspect。AspectJ 5 的缺点是需要学习扩展的 java 语言。但是 AspectJ 面向标签的编程模式使得将一个类转换成 aspect 变得很容易,只需要在类中加一些标签。

定义一个 aspect

以下是使用标签创建 AOP 的例子,这个例子在之前的文章中有提到过,观众在演出开始前后,以及出问题时,会自动做出一些反应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package concert;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class Audience {
@Before("execution(** concert.Performance.perform(..))")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}
@Before("execution(** concert.Performance.perform(..))")
public void takeSeats() {
System.out.println("Taking seats");
}
@AfterReturning("execution(** concert.Performance.perform(..))")
public void applause() {
 System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("execution(** concert.Performance.perform(..))")
public void demandRefund() {
System.out.println("Demanding a refund");
}
}

Audience 类上加上 @Aspect,用来表示 Audience 是一个 aspect,而 Audience 被标注的方法定义了具体的行为。在表演开始前,观众需要就座(takeSeats()),将手机静音(silenceCellPhones())。

表演结束后,观众需要鼓掌(applause()),如果表演过程中出现了异常,观众会要求退票(demandRefund())。AspectJ 提供了五个标签来定义 advice:

  • @After,在方法正常执行结束,或者出现异常的时候,执行 aspect。
  • @AfterReturning,在方法正常执行结束后,执行 aspect。
  • @AfterThrowing,在方法抛出异常的时候,执行 aspect。
  • @Around,在方法执行过程中,执行 aspect。
  • @Before, 在方法执行之前,执行 aspect。

上面的例子中,所有标签的值都是一个 pointcut 表达式,而且在这个例子里,正好是一样的(因为是作用在同一个方法上)。实际上,可以将这个 pointcut 定义好,然后进行引用,这样可以避免重复编写 pointcut。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Aspect
public class Audience {
@Pointcut("execution(** concert.Performance.perform(..))")
public void performance() {}

 @Before("performance()")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}
@Before("performance()")
public void takeSeats() {
System.out.println("Taking seats");
}
@AfterReturning("performance()")
public void applause() {
System.out.println("CLAP CLAP CLAP!!!");
}
@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Demanding a refund");
}
}

上面的代码,使用 @Pointcut 标签对 performance()方法进行标注,这样,就可以直接使用 performance()来代替 pointcut 表达式了。performance()只是一个标记,所以方法体可以也必须是空的。

如果只是定义了上面 Audience 这个 aspect, 那么其实什么也做不了。必须有一个配置文件,指出它是一个 aspect,并且解析这些标签,然后创建代理,最终将 Audience 转化为一个 aspect。

如果是使用 JavaConfig, 可以在配置文件类加上 @EnableAspectJAutoProxy 标签,以实现自动代理。以下是示例:

1
2
3
4
5
6
7
8
9
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
@Bean
public Audience audience() {
return new Audience();
}
}

Spring 使用 AspectJ 进行自动代理,仅仅是使用 @AspectJ 的标签作为指引,而底层仍然是 Spring 自己的基于代理的 aspect。所以,尽管使用了 @AspectJ 标签,Spring 的 AOP 仍然只能作用在方法级别。如果要使用 AspectJ 的全部功能,就必须在运行时注入 AspectJ,而不使用 Spring 来创建基于代理的 aspect。

创建一个 around advice

前面讲解了 before 和 after 的用法,由于 around 的用法有些不同,也更有用,所以这里单讲。先看看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Aspect
public class Audience {
@Pointcut("execution(** concert.Performance.perform(..))")
public void performance() {}

@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones");
System.out.println("Taking seats");
jp.proceed();
System.out.println("CLAP CLAP CLAP!!!");
} catch (Throwable e) {
System.out.println("Demanding a refund");
}
}
}

使用 @Around 标签标注了 watchPerformance 方法,监听 performance()代表的 joinpoint。此时,watchPerformance 的参数 ProceedingJoinPoint 就是指这个 joinpoint。可以看到,在 jp.proceed()的前后各有一些操作,甚至在抛出异常时,也有一些处理。所以,这个方法同时实现 @Before、@AfterReturning 和 @AfterThrowing 等标签的功能,更加灵活。

需要注意的是,必须执行 joinpoint 的 proceed()方法,否则,会导致被监听的方法没有执行。

我计划完成 50 到 100 篇关于 Spring 的文章,这是第十一篇,欢迎订阅 tantanit.com,第一时间获取文章更新,本文链接地址:http://tantanit.com/springinaction4-du-shu-bi-ji-zhi-shi-yong-biao-qian-chuang-jian-aop/

谈谈 IT的文章均为原创或翻译(翻译会注明外文来源),转载请以链接形式标明本文地址: http://tantanit.com/springinaction4-du-shu-bi-ji-zhi-shi-yong-biao-qian-chuang-jian-aop/

谈谈IT

欢迎关注官方微信公众号获取最新原创文章