import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
//切面 POJO
@Aspect
public class Audience {
//定义命名的切点
@Pointcut("execution(** com.springinaction.perf.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");
}
@Around("performance()") // 环绕通知方法
public void watchPerformance(ProceedingJoinPoint jp){
try {
System.out.println("Silencing cell phones Again");
System.out.println("Taking seats Again");
jp.proceed();
System.out.println("CLAP CLAP CLAP Again");
}
catch (Throwable e){
System.out.println("Demanding a refund Again");
}
}
}
关于环绕通知,我们首先注意到它接受ProceedingJoinPoint作为参数。这个对象是必须要有的,因为要在通知中通过它来调用被通知的方法。
需要注意的是,一般情况下,别忘记调用proceed()方法。如果不调用,那么通知实际上会阻塞对被通知方法的调用,也许这是所期望的效果。当然,也可以多次调用,比如要实现一个场景是实现重试逻辑。
除了注解和没有实际操作的performance()方法,Audience类依然是一个POJO,可以装配为Spring中的bean
@Bean
public Audience audience(){ // 声明Audience
return new Audience();
}
除了定义切面外,还需要启动自动代理,才能使这些注解解析。
如果使用JavaConfig的话,需要如下配置
package com.springinaction.perf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan
@EnableAspectJAutoProxy //启动AspectJ自动代理
public class AppConfig {
@Bean
public Audience audience(){ // 声明Audience
return new Audience();
}
}
假如在Spring中要使用XML来装配bean的话,那么需要使用Spring aop命名空间中的`<aop:aspectj-autoproxy>`元素。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=“http://www.springframework.org/schema/beans”
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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:component-scan base-package="com.springinaction.perf" />
<aop:aspectj-autoproxy />
<bean id="audience" class="com.springinaction.perf.Audience" />
无论使用JavaConfig还是XML,Aspect自动代理都会使用@Aspect注解的bean创建一个代理,这个代理会围绕着所有该切面的切点所匹配的bean。这种情况下,将会为Concert的bean创建一个代理,Audience类中的通知方法将会在perform()调用前后执行。
我们需要记住的是,Spring的AspectJ自动代理仅仅使用@AspectJ作为创建切面的指导,切面依然是基于代理的。本质上,它依然是Spring基于代理的切面。
**处理通知中的参数**
目前为止,除了环绕通知,其他通知都没有参数。如果切面所通知的方法确实有参数该怎么办呢?切面能访问和使用传递给被通知方法的参数吗?
为了阐述这个问题,我们来重新看一下BlankDisc样例。假设你想记录每个磁道被播放的次数。为了记录次数,