跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring AOP 注解实现详解

综述由AI生成Spring AOP 的基本概念及注解实现方式。涵盖了切点、连接点、通知、切面等核心概念,详细说明了五种通知类型(Around、Before、After 等)的使用场景。通过代码示例展示了如何配置依赖、定义切面、使用公共切点引用以及设置切点优先级。最后讲解了 execution 和 @annotation 两种切点表达式的语法及应用,帮助开发者掌握基于注解的 Spring AOP 编程模式。

GRACE Grace发布于 2026/3/30更新于 2026/5/2333 浏览
Spring AOP 注解实现详解

一、AOP 与 Spring AOP

AOP:Aspect Oriented Programming(面向方面编程)。是一种对某一类事情集中处理的思想。

Spring AOP:就是对 AOP 思想的一种实现。

二、Spring AOP 简单实现

我们简单实现一个统计每个接口的用时。

引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

写 AOP 实现:

  • 类使用注解@Aspect 修饰
  • 方法参数为 ProceedingJoinPoint 类,代表要实现的方法(只能在 Around 通知下写)
  • 方法使用注解@Around,参数是对应的路径的切点
  • ProceedingJoinPoint 的参数执行 proceed 方法。
package com.example.library.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class TimeAspect {
    @Around("execution(* com.example.library.controller.*.*(..) )")
    public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 开始时间
        long start = System.currentTimeMillis();
        // 执行方法
        Object result = proceedingJoinPoint.proceed();
        // 结束时间
        long end = System.currentTimeMillis();
        log.info("执行时间:" + (end - start) + "ms");
        return result;
    }
}

三、详解 Spring AOP

3.1 Spring AOP 核心概念

Spring AOP 核心概念:切点,连接点,通知,切面。

我们以上面的代码来介绍。

3.1.1 切点(Pointcut)

切点:就是告诉程序哪些方法需要使用到接下来的功能。
上面的@Around 注解的参数就是切点表达式。

文章配图

3.1.2 连接点(Join Point)

连接点:满足切点表达式规则的方法,就是连接点。也就是可以 AOP 控制的方法。

就像上面的代码的连接点就是:com.example.library.controller 路径下的所有方法。

切点和连接点的关系:连接点是满足切点表达式的元素。切点可以看做是保存了众多连接点的一个集合。

3.1.3 通知(Advice)

通知:这个 Spring AOP 方法要实现的功能就是通知。

就像上面的实现一个统计每个接口的用时的需求,就是通知。

文章配图

3.1.4 切面(Aspect)

切面 (Aspect) = 切点 (Pointcut) + 通知 (Advice)。

通过切面就能够描述当前 AOP 程序需要针对于哪些方法,在什么时候执行什么样的操作。
切面既包含了通知逻辑的定义,也包括了连接点的定义。

文章配图

3.2 通知类型

Spring 中 AOP 的通知类型有以下几种:

  • @Around:环绕通知,此注解标注的通知方法在目标方法前,后都被执行。
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行。
  • @After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行。
  • @AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行。
  • @AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行。

效果:

package com.example.demoaop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect
public class TestAspect {
    // 前置通知
    @Before("execution(* com.example.demoaop.*.*(..) )")
    public void testBefore() {
        log.info("Before 方法执行前");
    }

    // 后置通知
    @After("execution(* com.example.demoaop.*.*(..) )")
    public void testAfter() {
        log.info("After 方法执行后");
    }

    // 返回后通知
    @AfterReturning("execution(* com.example.demoaop.*.*(..) )")
    public void testAfterReturning() {
        log.info("AfterReturning 返回后通知");
    }

    // 抛出异常后通知
    @AfterThrowing("execution(* com.example.demoaop.*.*(..) )")
    public void testAfterThrowing() {
        log.info("AfterThrowing 抛出异常后通知");
    }

    // 环绕通知
    @Around("execution(* com.example.demoaop.*.*(..) )")
    public void testAround(ProceedingJoinPoint pjp) throws Throwable {
        log.info("Around 方法执行前");
        Object proceed = pjp.proceed();
        log.info("Around 方法执行后");
    }
}

文章配图

3.3 公共切点引用@PointCut

当我们的切点表达式是一样的时候,像上面我们还是在每一个通知类型的注解中,都使用了相同的表达式。
我们就可以使用方法注解@PointCut 将切点表达式提取出来,然后后面使用只需要写方法名即可。
在其他切点类中也可以调用,需要将@PointCut 注解所在类的路径写出来。

package com.example.demoaop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect
public class TestAspect {
    @Pointcut("execution(* com.example.demoaop.*.*(..) )")
    public void pc() {}

    // 前置通知
    @Before("pc()")
    public void testBefore() {
        log.info("TestAspect Before 方法执行前");
    }

    // 后置通知
    @After("pc()")
    public void testAfter() {
        log.info("TestAspect After 方法执行后");
    }
}
package com.example.demoaop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect
public class TestAspect2 {
    // 前置通知
    @Before("com.example.demoaop.TestAspect.pc()")
    public void testBefore() {
        log.info("TestAspect2 Before 方法执行前");
    }

    // 后置通知
    @After("pc()")
    public void testAfter() {
        log.info("TestAspect2 After 方法执行后");
    }
}

结果:
可以看见生效了,而且在其他切点类中只有加上了路径的才生效了。

文章配图

3.4 切点优先级@Order

我们定义 3 个一样的切点类,看他们的输出顺序:

文章配图

存在多个切面类时,默认按照切面类的类名字母排序:

  • @Before 通知:字母排名靠前的先执行
  • @After 通知:字母排名靠前的后执行
    但这种方不方便管理,我们的类名更多还是具备一定含义的。
    Spring 给我们提供了一个新的注解,来控制这些切面通知的执行顺序:@Order

我们将切点类的优先级换一下:

@Component
@Slf4j
@Aspect
@Order(1)
public class TestAspect3 {
    // 前置通知
    @Before("com.example.demoaop.TestAspect.pc()")
    public void testBefore() {
        log.info("TestAspect3 Before 方法执行前");
    }

    // 后置通知
    @After("com.example.demoaop.TestAspect.pc()")
    public void testAfter() {
        log.info("TestAspect3 After 方法执行后");
    }
}
@Component
@Slf4j
@Aspect
@Order(2)
public class TestAspect2 {
    // 前置通知
    @Before("com.example.demoaop.TestAspect.pc()")
    public void testBefore() {
        log.info("TestAspect2 Before 方法执行前");
    }

    // 后置通知
    @After("com.example.demoaop.TestAspect.pc()")
    public void testAfter() {
        log.info("TestAspect2 After 方法执行后");
    }
}
@Component
@Slf4j
@Aspect
@Order(3)
public class TestAspect {
    @Pointcut("execution(* com.example.demoaop.*.*(..) )")
    public void pc() {}

    // 前置通知
    @Before("pc()")
    public void testBefore() {
        log.info("TestAspect Before 方法执行前");
    }

    // 后置通知
    @After("pc()")
    public void testAfter() {
        log.info("TestAspect After 方法执行后");
    }
}

执行结果:

文章配图

规律:
@Order 注解标识的切面类,执行顺序如下:

  • @Before 通知:数字越小先执行
  • @After 通知:数字越大先执行

像下图的表示,箭头代表执行过程:

文章配图

3.5 切点表达式

切点表达式用来描述切点,常有以下两种类型的切点表达式:execution 和 @annotation

3.5.1 execution

语法:

execution(<访问修饰限定符> <返回类型> <包名。类名。方法名 (方法参数)> <异常>)

含义:

  • 访问修饰限定符:表示切点对应的方法的访问修饰限定符
  • 返回类型:表示切点对应的方法的返回类型
  • 包名。类名。方法名 (方法参数):表示切点对应的方法的路径及参数
  • 异常:表示切点对应的方法抛出的异常
  • 访问修饰限定符 和 异常 可以省略

切点表达式支持通配符表达:

  1. * 匹配任意字符,只匹配一个元素 (返回类型,包,类名,方法或者方法参数)
    1.1. 包名使用 * 表示任意包 (一层包使用一个 * )
    1.2. 类名使用 * 表示任意类
    1.3. 返回值使用 * 表示任意返回值类型
    1.4. 方法名使用 * 表示任意方法
    1.5. 参数使用 * 表示一个任意类型的参数
  2. .. 匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数
    2.1. 使用 .. 配置包名,标识此包以及此包下的所有子包
    2.2. 可以使用 .. 配置参数,任意个任意类型的参数

例子:

  • TestController 下的 public 修饰,返回类型为 String 方法名为 t1 的无参方法
execution(public String com.example.demo.TestController.t1())
  • 匹配 TestController 下的所有无参方法
execution(*com.example.demo.TestController.*())
  • 匹配 controller 包下所有的类的所有方法
execution(* com.example.demo.controller.*.*(..))
3.5.2 @annotation

当我们要落实到不同类下个几个方法,用上面的 execution 就有点捉襟见肘。
我们就可以使用自定义注解的方式以及另一种切点表达式 @annotation 来描述这一类的切点。

自定义注解:

  • 然后就跟我们前面使用的注解一样包含,生命周期@Retention,作用范围@Target,交给 Spring 管理。

在自定义类的时候选择 annotation:

文章配图

@Component
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {}

定义切面类:

  • 使用@Aspect 注解修饰,
  • 交给 Spring 管理
  • 在通知类型的注解中使用:@annotation(自定义注解路径) 作为参数。
@Slf4j
@Component
@Aspect
public class MyAspectDemo {
    @Around("@annotation(com.example.demoaop.MyAspect)")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        log.info("annotation 运行前");
        pjp.proceed();
        log.info("annotation 运行后");
    }
}

通过上面的方法,使用了自定义注解修饰的方法,就可以添加切面类的通知。

@RequestMapping("/test")
@RestController
@Slf4j
public class Test {
    @RequestMapping("/f1")
    public String f1() {
        log.info("f1");
        return "s1";
    }

    @MyAspect
    @RequestMapping("/f2")
    public Integer f2() {
        log.info("f2");
        return 1;
    }

    @RequestMapping("/f3")
    public Boolean f3() {
        log.info("f3");
        return false;
    }
}

访问 f2 f1 f3 的结果:

文章配图

目录

  1. 一、AOP 与 Spring AOP
  2. 二、Spring AOP 简单实现
  3. 三、详解 Spring AOP
  4. 3.1 Spring AOP 核心概念
  5. 3.1.1 切点(Pointcut)
  6. 3.1.2 连接点(Join Point)
  7. 3.1.3 通知(Advice)
  8. 3.1.4 切面(Aspect)
  9. 3.2 通知类型
  10. 3.3 公共切点引用@PointCut
  11. 3.4 切点优先级@Order
  12. 3.5 切点表达式
  13. 3.5.1 execution
  14. 3.5.2 @annotation
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Stable Diffusion v1.5 广告设计实战:电商主图与社交媒体 Banner
  • AI 辅助高水平学术论文写作:选题、写作、投稿与审稿全流程指南
  • Java 顺序表实现杨辉三角思路与代码
  • 7.1 自动化触发器:Trigger 机制与 Webhook 的妙用
  • Coze(扣子)100 个落地用途与发布使用指南
  • Git 核心概念解析:从版本控制到团队协作实战
  • Java 操作 Qoder 数据采集卡:连接、配置与采集示例
  • Python 文件组织实战:从路径抽象到安全归档
  • HTML 标签详解:构建网页骨架的核心语法与用法
  • 2026 传媒行业变革:Agent 成新入口,AIGC 重塑内容供给
  • 智能降重与 AIGC 检测功能解析:适配主流平台的合规解决方案
  • OpenClaw 龙虾机器人本地部署与配置指南
  • MiniMax 发布全球首份大模型年报,预判 2026 三大超级 PMF
  • Microsoft Visual C++ 运行库安装与 DLL 缺失修复指南
  • 具身导航 VLN 前沿论文汇总 (2023-2026)
  • Quartus 18.0 软件安装及 ModelSim 环境配置
  • C++ 手写 HTTP 服务器:从请求解析到响应构建
  • 数据结构:快速排序算法详解与实现
  • 利用 AI 助手实现自然语言转 SQL 查询与 DDL 生成
  • 豆包 Seedream 4.0 多图融合测评:田园犬与三花猫多场景生成

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online