💡 一则也许对你有用的小广告 🏆

欢迎飞飞程序员   ,你将获得:专属的实战项目(已更新的所有会员标识的项目都能学习) / 1v1 提问 / Java 学习路线 / PHP 学习路线 / 学习打卡 / 社群讨论

  • 正在进行中的项目:《FFBlog知识付费博客项目》 正在持续更新中,基于 Spring Boot 3.x + JDK 21...,点击查看 ;
  • 《从零开发:FFBlog知识付费博客项目(全栈开发)》 演示链接: https://ffblog.ffcxy.com/  ;

截止目前, 飞飞  正在疯狂爆肝实战项目,后续还会上新更多项目,目标是将所学知识开发成项目并且分享给大家,如知识付费系统, Ai系统, CMS系统,在线商城系统,等等 ,欢迎点击围观

AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,它是对 OOP(面向对象编程) 的补充。

简单来说,OOP 关注的是纵向的职责划分(比如把系统拆分成 Controller、Service、Mapper 等不同的类),而 AOP 关注的是横向的共性功能抽取(比如日志、事务、权限校验等,这些功能散落在各个业务方法中)。

AOP 的核心目标是:将横切关注点(Cross-Cutting Concerns)从业务逻辑中分离出来,提高代码的复用性和可维护性。


1. 为什么需要 AOP?

假设你有一个用户服务,里面有 注册、登录、修改密码 等方法。现在产品经理要求:每个方法执行前都要记录日志,执行后都要统计耗时。

没有 AOP 时(OOP 写法):

public class UserService {
    public void register() {
        long start = System.currentTimeMillis();
        System.out.println("开始执行 register");
        // ... 核心业务逻辑
        System.out.println("执行结束,耗时:" + (System.currentTimeMillis() - start));
    }

    public void login() {
        long start = System.currentTimeMillis();
        System.out.println("开始执行 login");
        // ... 核心业务逻辑
        System.out.println("执行结束,耗时:" + (System.currentTimeMillis() - start));
    }
    // ... 每个方法都要重复写这些日志代码
}

问题:日志代码和业务代码耦合在一起,大量重复,修改时要改很多地方。

使用 AOP 后:

@Service
public class UserService {
    public void register() {
        // ... 只写核心业务逻辑
    }

    public void login() {
        // ... 只写核心业务逻辑
    }
}

日志和耗时统计被抽取到一个切面中,Spring 会在运行时自动把这些逻辑“织入”到目标方法前后。


2. AOP 的核心概念

术语 英文 说明
切面 Aspect 横切关注点的模块化,比如一个日志切面类。
连接点 Join Point 程序执行过程中的某个点,比如方法调用、异常抛出。Spring AOP 只支持方法级别的连接点。
切点 Pointcut 匹配连接点的表达式,决定在哪些方法上应用切面。
通知 Advice 在连接点上执行的动作,比如“方法执行前打印日志”。
目标对象 Target 被切面织入的原始对象,也就是业务类。
代理 Proxy Spring AOP 通过动态代理生成的对象,包裹了目标对象。
织入 Weaving 将切面应用到目标对象并创建代理对象的过程。

3. 五种通知类型

Spring AOP 提供了 5 种通知(Advice):

通知类型 注解 执行时机
前置通知 @Before 目标方法执行前
后置通知 @After 目标方法执行后(无论是否异常)
返回通知 @AfterReturning 目标方法正常返回后
异常通知 @AfterThrowing 目标方法抛出异常后
环绕通知 @Around 目标方法执行前后都能控制,最强大

4. 一个完整的 Spring Boot AOP 示例

第一步:引入依赖

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

第二步:编写切面类

@Aspect
@Component
public class LogAspect {

    // 定义切点:匹配 com.example.service 包下所有类的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void servicePointcut() {}

    // 前置通知
    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("【前置】开始执行:" + joinPoint.getSignature().getName());
    }

    // 返回通知
    @AfterReturning(pointcut = "servicePointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("【返回】执行成功,返回值:" + result);
    }

    // 异常通知
    @AfterThrowing(pointcut = "servicePointcut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        System.out.println("【异常】执行出错:" + e.getMessage());
    }

    // 环绕通知(最常用,可以统计耗时)
    @Around("servicePointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed(); // 执行目标方法
        long end = System.currentTimeMillis();
        System.out.println("【环绕】方法 " + pjp.getSignature().getName() + " 耗时:" + (end - start) + "ms");
        return result;
    }
}

第三步:业务类(无需任何改动)

@Service
public class UserService {
    public String register(String username) {
        System.out.println("正在注册用户:" + username);
        return "注册成功";
    }
}

运行结果:

【前置】开始执行:register
正在注册用户:张三
【返回】执行成功,返回值:注册成功
【环绕】方法 register 耗时:2ms

5. 切点表达式(Pointcut Expression)

最常用的是 execution 表达式,语法如下:

execution(修饰符 返回值类型 包名.类名.方法名(参数类型))

常见示例:

表达式 含义
execution(* com.example.service.*.*(..)) service 包下所有类的所有方法
execution(public * *(..)) 所有 public 方法
execution(* com.example..*.*(..)) com.example 及其子包下所有方法
@annotation(com.example.annotation.Log) 所有标注了 @Log 注解的方法

6. Spring AOP 的实现原理

Spring AOP 底层使用动态代理:

代理方式 条件
JDK 动态代理 目标类实现了接口(默认)
CGLIB 代理 目标类没有实现接口(Spring Boot 默认使用 CGLIB)

Spring Boot 2.x 之后,默认统一使用 CGLIB 代理,即使有接口也用 CGLIB。


7. AOP 的典型应用场景

场景 说明
日志记录 统一记录方法调用日志、耗时。
事务管理 @Transactional 就是 AOP 的经典应用。
权限校验 方法执行前检查用户是否有权限。
缓存 @Cacheable 也是通过 AOP 实现的。
异常处理 统一捕获并处理异常。
参数校验 方法执行前校验参数合法性。

8. AOP vs OOP

对比项 OOP AOP
关注点 纵向:类、对象、继承 横向:切面、通知
解决问题 业务逻辑的模块化 横切关注点的模块化
典型场景 Service、Controller 分层 日志、事务、权限
关系 基础 补充,不替代 OOP

总结

特性 说明
定义 面向切面编程,对 OOP 的补充
核心思想 将横切关注点从业务逻辑中分离
核心概念 切面、切点、通知、连接点、代理
通知类型 @Before、@After、@AfterReturning、@AfterThrowing、@Around
实现原理 动态代理(JDK 或 CGLIB)
典型应用 日志、事务、权限、缓存
Spring Boot 依赖 spring-boot-starter-aop

一句话:AOP 就是让你在不修改业务代码的前提下,给方法“偷偷”加上额外功能的技术。

上一篇 DO Mapper Vo Controller Service 的关系一文说清楚
已是最后一篇

全部评论(0)

头像
😃 😁 😅 😂 😍 😜 😝 🤑 🥵 🥰 😙 😎 😵 😭 😱 😖 🥳 👽 🙈 🤡 😤 💣 💯 💢 ❤️ 👍 👏 👋 👌 🤏 🙏
还没有任何评论哟~
微信扫一扫
微信二维码
微信客服
QQ 咨询
QQ二维码
工作日 9:00 - 18:00 在线
QQ客服
在线工单
提交问题工单,我们会尽快回复处理。
提交工单
在线工单