Spring AOP
什么是AOP?
Aspect Oriented Programing ⾯向切⾯编程,相比较 oop ⾯向对象编程来说,Aop关注的不再是程
序代码中某个类,某些⽅法,而aop考虑的更多的是⼀种⾯到⾯的切⼊,即层与层之间的⼀种切⼊,所
以称之为切⾯。联想⼤家吃的汉堡(中间夹肉)。那么aop是怎么做到拦截整个⾯的功能呢?考虑前⾯
学到的servlet filter /* 的配置 ,实际上也是aop 的实现。
AOP能做什么?
AOP主要应⽤于⽇志记录,性能统计,安全控制,事务处理等方⾯,实现公共功能性的重复使用。
AOP的特点
- 降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合)
- 提⾼了代码的复⽤性。
- 提⾼系统的扩展性。(⾼版本兼容低版本)
- 可以在不影响原有的功能基础上添加新的功能
AOP的底层实现
动态代理(JDK + CGLIB)
AOP基本概念
5.5.1. Joinpoint(连接点)
被拦截到的每个点,spring中指被拦截到的每⼀个方法,spring aop⼀个连接点即代表⼀个方法的执
⾏。
5.5.2. Pointcut(切入点)
对连接点进⾏拦截的定义(匹配规则定义 规定拦截哪些⽅法,对哪些方法进⾏处理),spring 有专
⻔的表达式语⾔定义。
5.5.3. Advice(通知)
拦截到每⼀个连接点即(每⼀个方法)后所要做的操作
- 前置通知 (前置增强)— before() 执行方法前通知
- 返回通知(返回增强)— afterReturn 方法正常结束返回后的通知
- 异常抛出通知(异常抛出增强)— afetrThrow()
- 最终通知 — after 无论方法是否发⽣异常,均会执行该通知。
- 环绕通知 — around 包围⼀个连接点(join point)的通知,如方法调⽤。这是最强⼤的⼀种通知
类型。 环绕通知可以在方法调⽤前后完成⾃定义的行为。它也会选择是否继续执行连接点或直接
返回它们⾃⼰的返回值或抛出异常来结束执行。
5.5.4. Aspect(切面)
切⼊点与通知的结合,决定了切⾯的定义,切⼊点定义了要拦截哪些类的哪些方法,通知则定义了拦
截过方法后要做什么,切⾯则是横切关注点的抽象,与类相似,类是对物体特征的抽象,切⾯则是横切
关注点抽象。
5.5.5. Target(目标对象)
被代理的⽬标对象
5.5.6. Weave(织入)
将切⾯应⽤到⽬标对象并⽣成代理对象的这个过程即为织入
5.5.7. Introduction(引入)
在不修改原有应⽤程序代码的情况下,在程序运⾏期为类动态添加⽅法或者字段的过程称为引⼊
Spring AOP的实现
Spring AOP环境搭建
坐标依赖引⼊
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
添加spring.xml的配置
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
配置spring.xml文件
<!--配置AOP代理-->
<aop:aspectj-autoproxy/>
切入点:
匹配规则。规定什么方法被拦截、需要处理什么方法
定义切⼊点
@Pointcut(“匹配规则”)
Aop 切入点表达式简介
- 执行任意公共方法:
execution(public *(…))
- 执行任意的set方法
execution(* set*(…))
- 执行com.xxxx.service包下任意类的任意方法
execution(* com.xxxx.service..(…))
- 执行com.xxxx.service 包 以及⼦包下任意类的任意方法
execution(* com.xxxx.service….(…))
注:表达式中的第⼀个* 代表的是方法的修饰范围
可选值:private、protected、public (* 表示所有范围)
通知
@Pointcut("execution(* com.zwx.service..*.*(..))")
public void queryByName(){}
/**
* 声明前置通知 并将通知应⽤到定义的切⼊点上
* ⽬标类⽅法执⾏前 执⾏该通知
*
*/
@Before(value="queryByName()")
public void before(){
System.out.println("前置");
}
/**
* 声明返回通知 并将通知应⽤到定义的切⼊点上
* ⽬标类⽅法(⽆异常)执⾏后 执⾏该通知
*
*/
@AfterReturning(value="queryByName()")
public void afterReturn(){
System.out.println("返回通知");
}
/**
* 声明异常通知 并将通知应⽤到定义的切⼊点上
* ⽬标类⽅法出现异常时 执⾏该通知
*/
@AfterThrowing(value="queryByName()")
public void throwing(){
System.out.println("异常通知");
}
/**
* 声明最终通知 并将通知应⽤到定义的切⼊点上
* ⽬标类⽅法(⽆异常或有异常)执⾏后 执⾏该通知
*
*/
@After(value="queryByName()")
public void after1(){
System.out.println("最终通知");
}
/**
* 声明环绕通知 并将通知应⽤到切⼊点上
* ⽅法执⾏前后 通过环绕通知定义相应处理
* 需要通过显式调⽤对应的⽅法,否则⽆法访问指定⽅法 (pjp.proceed();)
* @param pjp
* @return
*/
@Around(value="queryByName()")
public void around(ProceedingJoinPoint point){
System.out.println("环绕--前");
try {
Object proceed = point.proceed();
System.out.println("环绕--返回");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕--异常");
}
System.out.println("环绕--最终");
}