Spring AOP 详细理解

一、前言

  • 什么是AOP。
  • Spring处理AOP时机什么样。
  • 如何生成代理对象。
  • 调用代理对象的方法时面对多重增强逻辑如何处理,优先级是什么。
  • AOP有哪些应用

二、体系认知

2.1 总体结构思维导图

请添加图片描述

2.2 流程图

请添加图片描述

2.3 基本通知(Advice)类图

实现了MethodInterceptor接口的类才能被调用。因此需要通过适配器模式将AspectJMethodBeforeAdvice和AspectJAfterReturningAdvice包装起来。

五种通知注解逻辑都是在下面几个实体类中实现的。

在这里插入图片描述

2.4 通知链的处理过程

请添加图片描述

可以将一个通知增强逻辑+被代理方法当成一个代理方法,每一层都对下一层的代理方法进行增强。

2.4.1 正常执行的顺序

由图可知,正常执行的顺序为:

  1. @Around的前置逻辑->下一层代理方法
  2. @Before的增强逻辑
  3. @AfterReturning的增强逻辑
  4. @After的增强逻辑
  5. @Around的后置逻辑

2.4.2 出现异常的执行顺序

根据上图可知,@AfterThrowing的Try之后的流程(真实被代理方法)发生了异常,会被Catch到,然后触发@AfterThrowing的增强逻辑。

  1. @Around的前置逻辑->下一层代理方法
  2. @Before的增强逻辑
  3. @AfterThrowing的增强逻辑
  4. @After的增强逻辑
  5. 无法执行@Around的后置逻辑,除非在@Around的增强方法中对下一层代理方法进行了try catch的处理。

2.4.3 关于发生异常时@After增强逻辑的触发时机

由图可知,

在@After的Try之后的流程(@AfterReturning增强逻辑、@AfterThrowing增强逻辑、真实代理方法)发生了异常,都能触发@After的增强逻辑,因为@After的增强逻辑写在了finally里。

在@After的Try之前的流程发生了异常,无法触发。

2.4.4 关于通知链对于异常的处理

即使@After将增强逻辑写在finally中,@AfterThrowing将异常catch了。

但是

  • @After用了两层try,内层try没catch异常,外层catch到异常后还会向上抛出异常。
  • @AfterThrowing将异常catch,调用完增强逻辑,再次向上抛出异常。

三、基本概念

3.1 AOP的术语

术语描述
Aspect(切面)一个模块具有一组提供横切需求的 APIs,简单来说就是封装用于横向插入系统功能的类。
Join point(连接点)在程序执行过程中的某个阶段点,你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。
Advice(通知/增强处理)AOP框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现。
Pointcut(切入点)切面与程序的交叉点,即那些需要被处理的连接点。即定义了这个Adivce 应用到哪些 Joinpoint 上。
Introduction(引入)引用允许你添加新方法或属性到现有的类中。也就是把切面(新方法属性:通知定义的)用到目标类中。
Target object(目标对象)指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。
Proxy(代理)增强后的代理类。
Weaving(织入)将 Advice 应用到 Target Object 上的整个过程叫织入。

3.1.1 注解

注解功能通知代理优先级(越小越先处理)处理逻辑的类描述
@Pointcut决定什么类或方法会被增强注解为了避免相同的匹配规则被定义多处,专门定义该方法设置执行的匹配规则,各个自行调用即可。
@Around方法执行前后进行增强1AspectJAroundAdvice环绕通知
@Before方法执行前执行增强逻辑。2MethodBeforeAdviceInterceptor.
AspectJMethodBeforeAdvice
前置通知。因AspectJMethodBeforeAdvice未实现MethodInterceptor接口,所以通过适配器转换
@After在方法执行结束后执行。获取不到返回值。
被代理方法及低于After优先级通知(AfterReturning/AfterThrowing)抛出异常,都可以执行After的方法。
高于After优先级的通知抛出异常,则After的增强逻辑无法执行。
3AspectJAfterAdvice最终通知
@AfterReturning在方法执行结束后执行。增强逻辑中可获取返回值。若方法抛出异常则无法执行。4AfterReturningAdviceInterceptor
AspectJAfterReturningAdvice
后置通知。因AspectJAfterReturningAdvice未实现MethodInterceptor接口,所以通过适配器转换
@AfterThrowing被代理方法抛出异常时会执行。5AspectJAfterThrowingAdvice异常通知
@Aspect管理增强逻辑标注当前类为一个切面类

3.1.2 @Pointcut表达式

官方文档

aop中Pointcut切入点指示符

3.2 通知优先级

请添加图片描述

在调用代理对象的方法时,会根据通知链递归调用。而通知链的默认顺序是[Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class],通知相同按照方法名称排序。

四、使用样例

POM依赖

 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.12.RELEASE</version>
 </dependency>
 <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.9.5</version>
 </dependency>

日志记录

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiLog {
    /**
     * 日志输出内容
     *
     * @return 输出内容
     */
    String value() default "";

    /**
     * 是否输出参数
     *
     * @return 是否输出参数
     */
    boolean showParams() default true;
}
@Slf4j
@Aspect
@Component
public class ApiLogAspect {
    /**
     * 绑定方法上的注解;结合swagger 注解 记录api日志;
     * @param joinPoint 切点
     * @param apiLog    消息注解
     * @return 返回值
     * @throws Throwable 抛出异常
     * @see ApiLogAspect#printLogForMethod
     */
    @Around("@annotation(apiLog)")
    public Object printLogForMethod(ProceedingJoinPoint joinPoint, ApiLog apiLog) throws Throwable {
        // 查询方法名称
        String methodDescription = findMethodDescription(joinPoint, apiLog);
        // 拼接消息并输出
        log.info(appendMessage(joinPoint, apiLog, methodDescription));
        // 方法计时
        return calcRunningTime(joinPoint, methodDescription);
    }
    private Object calcRunningTime(ProceedingJoinPoint joinPoint, String methodDescription) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Object result = joinPoint.proceed();
        stopWatch.stop();
        log.info(methodDescription + "方法执行时间为:{}ms", stopWatch.getTotalTimeMillis());
        return result;
    }
    private String findMethodDescription(ProceedingJoinPoint joinPoint, ApiLog apiLog) {
        String methodDescription = apiLog.value();
        if (StringUtils.isEmpty(methodDescription)) {
            // ------------------------
            MethodSignature ms = (MethodSignature) joinPoint.getSignature();
            methodDescription = ms.getName();
        }
        return methodDescription;
    }
    private String appendMessage(ProceedingJoinPoint joinPoint, ApiLog apiLog, String methodDescription) {
        StringBuilder message = new StringBuilder(methodDescription + ",开始执行!");
        if (apiLog.showParams()) {
            message.append("方法参数为:");
            // 获取参数对象
            Object[] params = joinPoint.getArgs();
            // 遍历参数对象,拼接信息
            for (Object param : params) {
                message.append("\n")
                        .append(param.getClass().getName())
                        .append(":")
                        .append(param.toString());
            }
        }
        return message.toString();
    }
}

五、通过源码理解

5.1 通过@EnableAspectJAutoProxy注册AnnotationAwareAspectJAutoProxyCreator

六、面试题及概要回答

七、参考文章

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值