0002-AOP基本属性
一、AOP——另一种编程思想
1.1 什么是AOP
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
1.2 为什么需要aop
AOP 能帮助我们做统一的处理,比如方法中重复的代码我们不必每个方法都写一遍,可以通过AOP进行统一处理,如方法执行时长,统一的日志处理等。
二、AOP 术语
AOP 领域中的特性术语:
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
三、在spring boot 中的使用
Spring 中的 AOP 是通过动态代理实现的。不同的框架实现方式不一样,但思想基本一致。
3.1 依赖
3.2 AOP 代码
package com.zhoust.fastdome.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author zhoust
* @Date 2021/9/22 22:52
* @Desc AOP 常用注解
*/
@Order(1)
@Slf4j
@Aspect
@Component
public class AspectDome {
/**
* 1、execution(): 表达式主体。
* 2、第一个*号:表示返回类型, *号表示所有的类型。
* 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.zhoust.fastdome.business.controller.scan包、子孙包下所有类的方法。
* 4、第二个*号:表示类名,*号表示所有的类。
* 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
*/
@Pointcut("execution(* com.zhoust.fastdome.aspect.TestForAspectDomeController.*(..))")
public void getPointCut(){
}
/**
* 前置通知(Before):在目标方法被调用之前调用通知功能
* 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
* 返回通知(After-returning):在目标方法成功执行之后调用通知
* 异常通知(After-throwing):在目标方法抛出异常后调用通知
* 环绕通知(Around):通知包裹了被通知的方法,在被通知的方
* 法调用之前和之后执行自定义的行为
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("getPointCut()")
public Object saveLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("=================Around 执行方法前,通过 joinPoint.proceed()执行方法==========================");
// 执行目标 获取返回值(注意joinPoint.proceed()只能执行一次,执行多次就调用多次方法)
Object result = joinPoint.proceed();
log.info("=================Around 执行方法后,joinPoint.proceed() 的返回值就是执行结果==========================");
return result;
}
@Before("getPointCut()")
public void before(){
log.info("==============Before 执行方法前执行==================");
}
@After("getPointCut()")
public void after(){
log.info("==============After 执行方法后执行,先于afterReturning==================");
}
@AfterReturning("getPointCut()")
public void afterReturning(){
log.info("==============afterReturning 执行方法后正常后执行==================");
}
@AfterThrowing("getPointCut()")
public void afterThrowing(){
log.info("==============AfterThrowing 方法执行异常后执行==================");
}
}
3.2.1 通知
前置通知(Before):在目标方法被调用之前调用通知功能
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和之后执行自定义的行为
上面代码拦截后执行结果
### 正常返回
2021-10-12 11:23:51.536 | INFO | [http-nio-8088-exec-1] |com.zhoust.fastdome.aspect.AspectDome | 46 | : =================Around 执行方法前,通过 joinPoint.proceed()执行方法==========================
2021-10-12 11:23:51.537 | INFO | [http-nio-8088-exec-1] |com.zhoust.fastdome.aspect.AspectDome | 54 | : ==============Before 执行方法前执行==================
2021-10-12 11:23:51.541 | INFO | [http-nio-8088-exec-1] |c.z.fastdome.aspect.TestForAspectDomeController | 20 | : controller中正常方法
2021-10-12 11:23:51.542 | INFO | [http-nio-8088-exec-1] |com.zhoust.fastdome.aspect.AspectDome | 49 | : =================Around 执行方法后,joinPoint.proceed() 的返回值就是执行结果==========================
2021-10-12 11:23:51.543 | INFO | [http-nio-8088-exec-1] |com.zhoust.fastdome.aspect.AspectDome | 58 | : ==============After 执行方法后执行,先于afterReturning==================
2021-10-12 11:23:51.543 | INFO | [http-nio-8088-exec-1] |com.zhoust.fastdome.aspect.AspectDome | 63 | : ==============afterReturning 执行方法后正常后执行==================
### 方法出错返回
2021-10-12 11:24:55.913 | INFO | [http-nio-8088-exec-5] |com.zhoust.fastdome.aspect.AspectDome | 46 | : =================Around 执行方法前,通过 joinPoint.proceed()执行方法==========================
2021-10-12 11:24:55.913 | INFO | [http-nio-8088-exec-5] |com.zhoust.fastdome.aspect.AspectDome | 54 | : ==============Before 执行方法前执行==================
2021-10-12 11:24:55.913 | INFO | [http-nio-8088-exec-5] |c.z.fastdome.aspect.TestForAspectDomeController | 29 | : controller中抛异常!
2021-10-12 11:24:55.914 | INFO | [http-nio-8088-exec-5] |com.zhoust.fastdome.aspect.AspectDome | 58 | : ==============After 执行方法后执行,先于afterReturning==================
2021-10-12 11:24:55.914 | INFO | [http-nio-8088-exec-5] |com.zhoust.fastdome.aspect.AspectDome | 67 | : ==============AfterThrowing 方法执行异常后执行==================
通知的顺序可以用下面伪代码表示,便于理解
try{
try{
//@Before
method.invoke(..);
}finally{
//@After
}
//@AfterReturning
}catch(){
//@AfterThrowing
}
3.2.2 连接点(join point)
在上面案例中 ProceedingJoinPoint 就相当于链接点,可以在这里获取请求参数等。
@Around("getPointCut()")
public Object saveLog(ProceedingJoinPoint joinPoint) throws Throwable {
3.2.3 切点(PointCut): 可以插入增强处理的连接点。
上述例子中@Pointcut() 就是切点,这个切点可以使用execution来指定范围,也可以使用注解指定如@Pointcut("@annotation(com.zhoust.fastdome.annnotation.LogAutoRecord)")
/**
* 1、execution(): 表达式主体。
* 2、第一个*号:表示返回类型, *号表示所有的类型。
* 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.zhoust.fastdome.business.controller.scan包、子孙包下所有类的方法。
* 4、第二个*号:表示类名,*号表示所有的类。
* 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
*/
@Pointcut("execution(* com.zhoust.fastdome.aspect.TestForAspectDomeController.*(..))")
public void getPointCut(){
}
给注解类加上 @Order(1)
当有多个切面时,可以通过order来指定 执行的顺序,值越小优先级越高。
参考:

浙公网安备 33010602011771号