- Spring提供了4种类型的AOP支持:
- 基于代理的经典SpringAOP
- 纯POJO切面
- @AspectJ注解驱动的切面
- 注入式AspectJ切面
通知:需要调用的方法
连接点:可以定义调用通知的所有位置
切点:连接点的子集,需要自定义匹配的位置
切面:通知和切点共同定义了切面的全部内容
引入:允许我们向现有的类添加新方法或属性
织入:把切面应用到目标对象并创建新的代理对象的过程,在目标对象的声明周期种有多个点可以织入:编译器,类加载器,运行期。
- Spring通知是java编写的,在运行时通知对象,Spring只支持方法级别的连接点,如超过方法需求要用第四种AOP支持
- Spring 的 AspectJ 自动代理仅仅使用@AspectJ 作为创建切面的知道,切面依然是基于代理的。本质上,它依然是基于代理的切面。
本文使用@Aspect 注解驱动的切面
- 在AOP编程中有五种类型的通知:
- 前置通知 (@Before) 方法执行之前执行
- 返回通知 (@AfterReturning) 方法return之后执行
- 异常通知 (@AfterThrowing) 方法出现异常之后执行
- 后置通知 (@After) : 又称之为最终通知(finally)
- 环绕通知 (@Around) :重点掌握
其结构如下:
try{
@Before
//核心业务
@AfterReturning
}catch(Exception e){
// ….
@AfterThrowing
}finally{
//….
@After
}
- 实例:看表演
看表演前观众需要关手机,找座位,看表演后鼓掌,若表演失败了观众要求重演。
正常思路是调用方法。AOP思想是声明通知,把通知接入切点中成为切面。即重点是看表演,在看表演的前后,自动调用方法(通知)。
1.接口
package com.qhf.aop.example01;
public interface Performance {
public void perform();
}
2.实现接口的表演类
package com.qhf.aop.example01;
import org.springframework.stereotype.Component;
@Component
public class PerformanceImpl implements Performance{
@Override
public void perform() {
//int a = 1/0;
System.out.println("表演过程中...");
}
}
3.配置类或配置文件xml
package com.qhf.aop.example01;
import org.springframework.context.annotation.*;
/**
* 使用AspectJ自动代理两种方式:二选一
* 1.配置类注解@EnableAspectJAutoProxy 启动AspectJ自动代理
* 2.使用配置文件需要在配置类注解@ImportResource("classpath:aop/aopxml.xml"),xml中<aop:aspectj-autoproxy/>
*/
@Configuration//作为配置文件之一
//@ImportResource("classpath:aop/aopxml.xml")
@EnableAspectJAutoProxy
@ComponentScan //自动扫描同包下@Component注解的实现类
public class AOPConfig {
}
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
</beans>
4.声明切面:@Aspect 注解使用AspectJ定义切面,同时此观众类是一个POJO,它是可以被正常地自动装配,并且切面自动代理是通过调用方法的,所以还是要使用@Component 注解作为组件类,以便被自动装配为bean。
切点表达式execution(* com.qhf.aop.example01.Performance.perform(..)):*表示可以是任何类型的返回值,com.qhf.aop.example01 是包名,Performance 类名或接口,perform是方法,(..)表示任何参数,所以运行时匹配这个规则的执行都会嵌入这个切面。
@Before("execution(* com.qhf.aop.example01.Performance.perform(..)) && !bean(performanceImpl)") 表示并且不是id为performanceImpl实例运行
通知@Before在执行前;@AfterReturning在执行返回后;@AfterThrowing在执行错误后;@Around会将目标方法封装起来,通过ProceedingJoinPoint 的proceed() 方法自定义通知在目标方法前后;@After 在目标方法返回或抛出后调用
package com.qhf.aop.example01;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class Audience {
/**
* 当切点表达式使用频繁的时候,"execution(* com.qhf.aop.example01.Performance.perform(..))"
* 可以通过@Pointcut注解声明频繁使用的切点表达式,然后在其他方法注解调用此切点
*/
@Pointcut("execution(* com.qhf.aop.example01.Performance.perform(..))")
public void pointcut(){
}
//表演前
@Before("pointcut()")
//@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
public void silencePhones(){
System.out.println("关手机...");
}
@Before("pointcut()")
//@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
public void takeSeats(){
System.out.println("找座位...");
}
//表演后
@AfterReturning("pointcut()")
//@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
public void applause(){
System.out.println("鼓掌...");
}
//出现异常后
@AfterThrowing("pointcut()")
//@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
public void demandRefund(){
System.out.println("要求重演...");
}
}
或者使用@Around 环绕通知
@Aspect
@Component
public class Audience {
@Pointcut("execution(* com.qhf.aop.example02.Performance.perform(..))")
public void pointcut(){
}
@Around("pointcut()")
public void watchPerformance(ProceedingJoinPoint pj){
try {
silencePhones();
takeSeats();
pj.proceed();//运行方法
applause();
} catch (Throwable t){
demandRefund();
t.printStackTrace();
}
}
//表演前
public void silencePhones(){
System.out.println("关手机...");
}
//表演前
public void takeSeats(){
System.out.println("找座位...");
}
//表演后
public void applause(){
System.out.println("鼓掌...");
}
//出现异常后
public void demandRefund(){
System.out.println("要求重演...");
}
}
5.测试
package com.qhf.aop.example01;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= AOPConfig.class)
public class Test {
@Autowired
private Performance performance;
@org.junit.Test
public void test(){
performance.perform();
}
}
6.运行结果
关手机...
找座位...
表演过程中...
鼓掌...
若是表演类中执行 int a = 1/0;抛错,结果为:
关手机...
找座位...
要求重演...
java.lang.ArithmeticException: / by zero