在Spring实战一书中看到Spring提供了4中类型的AOP支持:
1. 基于代理的经典Spring AOP;
2. POJO切面;
3. @AspectJ注解驱动的切面;
4. 注入式AspectJ切面(适用于Spring各版本);
上一节的例子就是POJO切面的方式实现Spring AOP.
@AspectJ创建切面
1. @AspectJ定义切面,这里注意一点,@Before和@After里面放的是切点
package aop.kind3;
import org.aspectj.lang.annotation.*;
@Aspect
public class Audience {
@Pointcut("execution(* aop.kind3.Performance.perform(..))")
public void performance() {} // 定义切点
@Before("performance()")
public void takeSeats() {
// 节目开始之前
System.out.println("Taking seats");
}
@Before("performance()")
public void turnOffCellPhones() {
// 节目开始之前
System.out.println("Slicening cell phone");
}
@AfterReturning("performance()")
public void applaud() {
// 节目成功结束之后
System.out.println("CLAP CLAP CLAP");
}
@AfterThrowing("performance()")
public void demandRefund() {
// 节目表演失败之后
System.out.println("Demanding a refund");
}
}
Audience类不仅仅是一个pojo(plain ordinary java object),还是一个切面,我们知道一个切面需要有两部分组成,切点和通知,切点定义了在何处,通知定义了在何时和做什么,这样就完成了切面的定义。
注解 | 通知 |
@After | 通知方法会在目标方法返回或抛出异常后调用 |
@AfterReturning | 通知方法会在目标方法返回后调用 |
@AfterThrowing | 通知方法会在目标方法抛出异常后调用 |
@Around | 通知方法会将目标方法封装起来 |
@Before | 通知方法会在目标方法调用之前执行 |
除了注解和没有实际操作的performance()方法,Audience类依然是一个POJO,可以像其他类一样使用,单元测试。Audience只是一个Java类,只不过它通过注解表明会作为切面使用而已。
基于xml的方式使用
在XML中,通过Spring的aop命名空间启用AspectJ自动代理
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<!-- 添加<aop:aspectj-autoproxy />实现注解切面 -->
<aop:aspectj-autoproxy />
<bean id="drama" class="aop.kind3.Drama" />
<bean id="audience" class="aop.kind3.Audience" />
</beans>
创建环绕通知
环绕通知编写的逻辑将被通知的目标方法完全包装起来。实际上就像在一个通知方法中同时编写前置通
知和后置通知。实现:重写Audience切面。
package aop.kind3.around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class Audience {
@Pointcut("execution(* aop.kind3.Performance.perform(..))")
public void performance() {} // 定义切点
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Slicening cell phone");
System.out.println("Taking seats");
jp.proceed();
System.out.println("CLAP CLAP CLAP");
}catch (Throwable e){
System.out.println("Demanding a refund");
}
}
}
知识点:
1. @Around表示环绕注解,watchPerformance()方法会作为performance()切点的环绕通知。
2. ProceedingJoinPoin jp作为参数,这个对象是必须要有的,因为你要在通知中通过jp来调用被通知的方法。