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来指定 执行的顺序,值越优先级越高。

参考:

Spring AOP——Spring 中面向切面编程 - SharpCJ - 博客园 (cnblogs.com)

AOP切面的优先级Order属性_codingCoge的博客-CSDN博客

posted @ 2021-10-12 23:35  神经哇  阅读(127)  评论(0)    收藏  举报