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

在之前的读书笔记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/

© 2022 谈谈IT All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero