一文详解Spring Aop @After(后置通知)的使用场景

 更新时间:2025年06月20日 09:09:23   作者:冰糖心书房  
@After 是 Spring AOP 中的另一种通知(Advice)类型,通常被称为“后置通知”或“最终通知”,本文将通过详细的代码示例给大家介绍一下Spring Aop @After(后置通知)的使用场景,需要的朋友可以参考下

核心定义

@After 是 Spring AOP 中的另一种通知(Advice)类型,通常被称为“后置通知”或“最终通知”。

它的核心作用是:

无论目标方法是正常执行完成,还是在执行过程中抛出了异常,@After 通知中的代码 总是 会在目标方法执行之后被执行。

最经典的类比就是 Java 中的 try...catch...finally 语句块里的 finally 部分。@After 的行为和 finally 块的行为几乎一模一样。

@After 通知的执行流程

为了更好地理解,我们来看两种情况下的执行顺序:

情况一:目标方法成功执行

  • @Before 通知执行。
  • 目标方法 (targetMethod()) 执行并正常返回。
  • @After 通知执行。
  • (如果定义了)@AfterReturning 通知执行。

情况二:目标方法抛出异常

  • @Before 通知执行。
  • 目标方法 (targetMethod()) 执行,中途抛出异常。
  • @After 通知执行。
  • (如果定义了)@AfterThrowing 通知执行。
  • 异常继续向上层调用栈抛出。

一个非常关键的点是:@After 通知本身无法访问目标方法的返回值(因为它可能根本没有返回值,比如抛异常时),也无法捕获或处理从目标方法中抛出的异常。它只是一个保证“最后一定会被执行”的钩子。

@After 通知能做什么?(主要应用场景)

后置通知非常适合执行那些必须进行的“清理”或“收尾”工作,无论业务逻辑成功与否。

资源释放 (Resource Cleanup)

  • 这是 @After 最重要、最常见的用途。类似于 finally 块。
  • 示例:释放文件句柄、关闭网络连接、关闭数据库连接池中的连接等。确保即使业务代码出错,关键资源也不会被泄露。

上下文清理 (Context Cleanup)

  • 如果在 @Before 通知中向 ThreadLocal 存放了数据,那么在 @After 通知中将其 remove() 是一个最佳实践。这可以防止在线程池环境中发生内存泄漏或数据错乱。

最终日志记录 (Final Auditing)

  • 记录一个操作的结束。
  • 示例:“方法 updateProduct 执行完毕。” 这个日志不关心成功或失败,只记录“结束”这个事实。

性能监控 (Performance Monitoring)

  • 可以在 @Before 中记录一个开始时间,然后在 @After 中记录结束时间,并计算总耗时。

示例:

  • @Before: long startTime = System.currentTimeMillis(); (存入 ThreadLocal)
  • @After: long endTime = System.currentTimeMillis(); long duration = endTime - startTime; log.info("方法耗时: {} ms", duration);

与 @AfterReturning 和 @AfterThrowing 的区别

这是新手很容易混淆的地方,理解它们的区别至关重要:

知类型执行时机能否访问返回值?能否访问异常?主要用途
@After (最终通知)总是在目标方法后执行(无论成功或失败)不能不能资源清理、最终日志
@AfterReturning (返回通知)仅在目标方法成功执行后执行可以不适用基于返回结果的附加操作
@AfterThrowing (异常通知)仅在目标方法抛出异常后执行不适用可以异常日志记录、告警通知

简单来说:

  • 总是执行清理?用 @After
  • 想在成功后根据返回值做点事?用 @AfterReturning
  • 想在失败后专门处理异常?用 @AfterThrowing

代码示例

我们扩展之前的例子,增加一个删除方法(可能会失败),并为所有方法添加 @After 通知。

1. 业务服务类 (目标对象)

package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    // 成功执行的例子
    public String findUserById(Long id) {
        System.out.println("--- 核心业务逻辑:正在根据 ID 查询用户... ---");
        return "User" + id;
    }

    // 抛出异常的例子
    public void deleteUser(Long id) {
        System.out.println("--- 核心业务逻辑:正在尝试删除用户... ---");
        if (id <= 0) {
            throw new IllegalArgumentException("用户ID无效,删除失败!");
        }
        System.out.println("用户 " + id + " 已被成功删除。");
    }
}

2. 切面类 (Aspect) 中定义 @After 通知

package com.example.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Arrays;

@Aspect
@Component
public class LoggingAspect {

    @Pointcut("execution(public * com.example.service.*.*(..))")
    public void serviceLayerPointcut() {}

    // 前置通知
    @Before("serviceLayerPointcut()")
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("==================================================");
        System.out.printf("[AOP 前置通知]: 方法 [%s] 即将执行... 参数: %s%n", methodName, Arrays.toString(args));
    }

    // 后置通知 (最终通知)
    @After("serviceLayerPointcut()")
    public void logAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("[AOP 后置通知]: 方法 [%s] 执行完毕。执行清理工作...%n", methodName);
        System.out.println("--------------------------------------------------\n");
    }
}

3. 运行代码并观察输出

调用成功的方法 userService.findUserById(101L):

==================================================
[AOP 前置通知]: 方法 [findUserById] 即将执行... 参数: [101]
--- 核心业务逻辑:正在根据 ID 查询用户... ---
[AOP 后置通知]: 方法 [findUserById] 执行完毕。执行清理工作...
--------------------------------------------------

@After 在方法成功后执行了。

调用失败的方法 userService.deleteUser(0L) (需要用 try-catch 捕获异常):

try {
    userService.deleteUser(0L);
} catch (Exception e) {
    System.err.println("在调用方捕获到异常: " + e.getMessage());
}

输出:

==================================================
[AOP 前置通知]: 方法 [deleteUser] 即将执行... 参数: [0]
--- 核心业务逻辑:正在尝试删除用户... ---
[AOP 后置通知]: 方法 [deleteUser] 执行完毕。执行清理工作...
--------------------------------------------------

在调用方捕获到异常: 用户ID无效,删除失败!

即使 deleteUser 抛出了异常,@After 通知 (logAfter 方法) 依然被执行了,完美地展示了其 finally 的特性。

总结

特性描述
执行时机无论成功或失败,总是在目标方法执行之后执行。
核心用途资源释放上下文清理、最终日志记录等收尾工作。
行为类似Java 的 finally 语句块。
关键限制无法访问目标方法的返回值,也无法捕获或修改异常。
关键参数可以注入 JoinPoint 对象,获取方法元数据。

以上就是一文详解Spring Aop @After(后置通知)的使用场景的详细内容,更多关于Spring Aop @After使用场景的资料请关注脚本之家其它相关文章!

相关文章

  • 基于JVM 中常见垃圾收集算法介绍

    基于JVM 中常见垃圾收集算法介绍

    下面小编就为大家分享一篇基于JVM 中常见垃圾收集算法介绍,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • SpringMVC和Ajax的交互详解(手工处理)

    SpringMVC和Ajax的交互详解(手工处理)

    Ajax即异步的 JavaScript和XML,是一种无需重新加载整个网页的情况下,能够更新部分模块的网页技术,下面这篇文章主要给大家介绍了关于SpringMVC和Ajax交互的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • java新手入门——String类详解

    java新手入门——String类详解

    在java、C#中,String类是不可变的,对String类的任何改变,都是返回一个新的String类对象。string>是C++标准程序库中的一个头文件
    2021-06-06
  • Java集合框架概览之ArrayList源码刨析

    Java集合框架概览之ArrayList源码刨析

    本文主要从一个简单的代码入手,对ArrayList 的最常用的源码进行刨析,文中源码摘自 JDK1.8,源码的注释部分博主也加入了自己的翻译,欢迎读者食用
    2023-03-03
  • Java使用ffmpeg和mencoder实现视频转码

    Java使用ffmpeg和mencoder实现视频转码

    这篇文章主要为大家详细介绍了Java使用ffmpeg和mencoder实现视频转码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • Java装饰者模式的示例详解

    Java装饰者模式的示例详解

    装饰者模式:在不改变原有对象的基础之上,动态的将功能附加到对象上,提供了继承更有弹性的替代方案,也体现了开闭原则。本文将通过示例详细讲解一下装饰者模式,需要的可以参考一下
    2022-02-02
  • Java十分钟精通类 封装 继承

    Java十分钟精通类 封装 继承

    基础不牢地动山摇,类、封装、继承是我们在学习基础时必然要碰到的知识点,让我们一起来学习或回顾一下,感兴趣的伙伴来看看吧
    2022-03-03
  • MyBatis自定义typeHandler的完整实例

    MyBatis自定义typeHandler的完整实例

    这篇文章主要给大家介绍了关于MyBatis自定义typeHandler的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MyBatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • 如何在Java Deeplearning4j中进行数据加载与预处理

    如何在Java Deeplearning4j中进行数据加载与预处理

    本文介绍了如何在 Deeplearning4j 中加载和预处理数据,包括图像和 CSV 数据的加载,以及基本的数据标准化和分割操作,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • Java超详细讲解三大特性之一的多态

    Java超详细讲解三大特性之一的多态

    多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定
    2022-05-05

最新评论