Spring AOP通知(Advice)详解

简介: Spring 的 AOP 功能中一个关键概念是通知(Advice),与切点(Pointcut)表达式相关联在特定节点织入一些逻辑,Spring 提供了五种类型的通知。


一、概述



AOP 中的通知是基于连接点(Join point)业务逻辑的一种增强,Spring AOP 提供了下面五种通知类型:

  • Before advice(前置通知):连接点前面执行,不能终止后续流程,除非抛异常
  • After returning advice(后置通知):连接点正常返回时执行,有异常不执行
  • Around advice(环绕通知):围绕连接点前后执行,也能捕获异常处理
  • After advice(最终通知):连接点退出时执行,无论是正常退出还是异常退出
  • After throwing advice(异常通知):连接点方法抛出异常时执行

AOP 的连接点一般是指目标类的方法,五种通知类型执行的节点如下:


微信图片33.png


二、通知的定义



Spring AOP 可以基于 XML 方式和基于注解方式定义,只是写法不同,这里只使用注解的方式来讲解通知的详细用法。


1. 前置通知


@Aspect 切面类中使用 @Before 注解简单地定义一个前置通知。


@Aspect
@Component
public class DemoAspect {
    @Before("execution(* cn.codeartist.spring.aop.advice.*.*(..))")
    public void doBefore() {
        // 自定义逻辑
    }
}


2. 后置通知


方法正常返回,会执行后置通知,使用 @AfterReturning 注解定义后置通知。


@AfterReturning("execution(* cn.codeartist.spring.aop.advice.*.*(..))")
public void doAfterReturning() {
    // 自定义逻辑
}

注解的 returning 属性可以绑定目标方法返回值,用于在通知中获取目标方法执行完成后的返回结果。

@AfterReturning(pointcut = "pointcut()", returning = "retVal")
public void doAfterReturning(Object retVal) {
    // 自定义逻辑(通过参数绑定方法切入点方法的返回值)
}

当注解使用了 returning 属性时,切入点会增加返回值类型的限制,上面使用的 Object 类型可以匹配到所有返回值类型的目标方法。

例如下面的情况,后置通知的代码不会被执行:

// 目标方法
public String doService() {
    return "码匠公众号";
}
// 后置通知定义
@AfterReturning(pointcut = "pointcut()", returning = "retVal")
public void doAfterReturning(Integer retVal) {
    // 目标方法返回值是String类型,结果参数绑定类型是Integer,该通知不会被执行
}


3. 环绕通知


环绕通知可以在方法执行的任何节点添加逻辑,它可以实现另外 4 种通知的功能。

如果需要以线程安全的方式在方法执行前后共享状态,可以使用环绕通知。

@Around 注解来定义环绕通知,需要使用 ProceedingJoinPoint 作为参数,来执行目标方法调用。

@Around("execution(* cn.codeartist.spring.aop.advice.*.*(..))")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    // 方法执行前逻辑
    Object retVal = joinPoint.proceed();
    // 方法执行后逻辑
    return retVal;
}

joinPoint.proceed() 会调用目标方法,或者是调用另一个切面。

虽然环绕通知可以实现另外几种通知的功能,但在使用中都能实现功能的情况下,优先使用其他通知方式。


4. 最终通知


最终通知在方法退出的时候执行,使用 @After 注解定义,最终通知在方法正常退出和抛出异常时都会执行,通常用于资源的释放。

@After("execution(* cn.codeartist.spring.aop.advice.*.*(..))")
public void doAfter() {
    // 自定义逻辑
}


5. 异常通知


方法抛出异常的时候会执行异常通知,使用 @AfterThrowing 定义异常通知。

@AfterThrowing("execution(* cn.codeartist.spring.aop.advice.*.*(..))")
public void doAfterThrowing() {
    // 自定义逻辑
}

注解的 throwing 属性用来绑定目标方法抛出的异常,用于在通知中获取目标方法抛出的异常实例。

@AfterThrowing(pointcut = "pointcut()", throwing = "ex")
public void doAfterThrowing(Throwable ex) {
    // 自定义逻辑(通过参数绑定方法切入点方法抛出的异常)
}

当注解使用了 throwing 属性时,切入点会增加异常类型的限制,上面使用的 Throwable 类型可以匹配到所有异常类型。

例如下面的情况,异常通知的代码不会被执行:

// 目标方法
public void doServiceThrow() {
    throw new RuntimeException("Test exception");
}
// 异常通知定义
@AfterThrowing(pointcut = "pointcut()", throwing = "ex")
public void doAfterThrowing(NullPointerException ex) {
    // 目标方法抛出的是RuntimeException,参数绑定类型是NullPointerException,该通知不会被执行
}


三、通知的参数



在定义通知的方法签名上可以指定参数来绑定目标方法的一些信息(例如前面讲到的后置通知和异常通知)。


1. 切入点


在定义通知方法的时候,一般可以使用 JoinPoint 作为参数,环绕通知使用 ProceedingJoinPoint。常用接口方法如下:


JoinPoint

public interface JoinPoint {
  // 获取代理对象
    Object getThis();
    // 获取目标对象
    Object getTarget();
    // 获取连接点方法的参数
    Object[] getArgs();
    // 获取连接点方法的签名
    Signature getSignature();
}


ProceedingJoinPoint

public interface ProceedingJoinPoint extends JoinPoint {
    // 执行下一个切面的通知或者目标方法
    public Object proceed() throws Throwable;
    // 执行下一个切面的通知或者目标方法(带参数)
    public Object proceed(Object[] args) throws Throwable;
}

切面的切入点一般为方法,所以 Signature 可以转换为 MethodSignature 使用。


2. 通知的参数传递


通知的参数可以通过切点表达式 args 来指定,具体用法会在后面切点表达式详解中讲到。

@Before("pointcut() && args(name,..)")
public void doBefore(String name) {
    // 切点表达式增加参数匹配
}

args(name,..) 也会增加切入点的限制,目标方法的参数个数至少为一个,且第一个参数类型为 String


四、通知的顺序



Spring AOP 中一个目标类可以被多个切面切入,多个切面也可以切入一个目标类。

使用 @Order 注解来指定切面的优先级,来控制切面的执行顺序。

在注册切面 Bean 的时候指定 @Order,如下:

@Order(1)
@Aspect
@Component
public class FirstAspect {
    // ......
}

优先级高的切面先执行,通知执行的顺序如下:


微信图片22.png

可以得出:

  • 优先级高的切面,前置通知先执行
  • 优先级低的切面,后置通知先执行

Order 值越小,优先级越大。

Spring AOP 是基于动态代理的拦截器模式实现的,切面模型与拦截器模型相似,如下:

微信图片11.png

五、附录



1. 常用注解


注解 描述
@Aspect 定义切面类
@Before 定义前置通知
@AfterReturning 定义后置通知
@Around 定义环绕通知
@After 定义最终通知
@AfterThrowing 定义异常通知


2. 示例代码


Gitee 仓库:

https://gitee.com/code_artist/spring




目录
相关文章
|
5天前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
13天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
56 8
|
2月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
96 5
|
2月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
91 8
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
53 5
|
2月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
59 4
|
3月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
61 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
2月前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
51 1