Spring AOP 全面详解(通俗易懂 + 核心知识点 + 完整案例)

Spring AOP 全面详解(通俗易懂 + 核心知识点 + 完整案例)

一、Spring AOP 是什么 & 核心价值

1. 基础定义

AOP 是 Aspect Oriented Programming 的缩写,即面向切面编程,它是和 OOP(面向对象编程)互补的一种编程思想,并非替代 OOP。

Spring AOP 是 Spring 框架对 AOP 思想的落地实现,是 Spring 两大核心特性(IOC、AOP)之一。

2. 核心解决的问题(核心价值)

OOP 的核心是封装、继承、多态 ,核心逻辑是「从上到下」的纵向业务流程(比如:用户登录→校验参数→调用业务→返回结果),它擅长处理业务核心逻辑

但实际开发中,有大量 非业务核心、但多个业务都需要重复写的代码(比如:日志打印、事务控制、权限校验、性能监控、异常统一处理),这些代码被称为 「横切逻辑」

如果没有 AOP,我们会把这些横切逻辑硬编码到每一个业务方法中,会造成:

代码冗余:相同的日志 / 校验代码写几十遍;

维护困难:要改日志格式,需要改所有业务方法;

代码污染:业务方法中混着大量非业务代码,核心逻辑不清晰。

Spring AOP 的核心价值:无侵入的统一处理横切逻辑

横切逻辑与业务逻辑完全解耦,只写一次,全局生效;

业务代码只关注「核心业务」,代码干净、优雅;

横切逻辑的修改只需改一处,维护成本极低。

简单打个比方:

先搞懂:AOP 解决的是啥 "麻烦事"

假设你开了一家餐厅(对应 一个项目),店里有 3 个核心岗位:

后厨厨师:负责做菜(对应 核心业务逻辑:比如用户下单、支付、退款)

前台服务员:负责点餐(对应 核心业务逻辑:比如商品查询、库存扣减)

收银员:负责收钱(对应 核心业务逻辑:比如订单结算、账单生成)

正常情况下,厨师只管做菜、服务员只管点餐、收银员只管收钱,各司其职。

但餐厅有 3 个 "所有人都要遵守的规矩"(对应 项目中的横切逻辑:日志、权限、事务):

上班前必须打卡签解决到(对应:接口调用前打印请求日志)

工作时必须戴口罩手套(对应:接口调用时做权限校验)

下班后必须打扫卫生(对应:接口调用后释放资源、记录响应日志)

没有 AOP 的 "糟糕做法"

如果没有 AOP 思想,你会让 每个岗位的人都自己完成这 3 件事:

厨师:打卡 → 做菜 → 打扫卫生

服务员:打卡 → 点餐 → 打扫卫生

收银员:打卡 → 收钱 → 打扫卫生

问题来了:

重复劳动:3 个人做的 "打卡、戴口罩、打扫" 都是一模一样的事,却要各自做一遍;

维护麻烦:如果以后要求 "下班前要消毒",你得挨个通知厨师、服务员、收银员,改起来超费劲;

偏离主业:厨师本来该专心研究菜谱,结果还要操心打卡和打扫,核心工作被干扰。

有了 AOP 的 "聪明做法"

AOP 就相当于你雇了一个 "餐厅管理员"(对应 切面类),专门负责管这 3 个通用规矩:

管理员早上站在门口,统一检查所有人打卡(对应:前置通知,在核心业务前执行);

管理员在工作区巡逻,统一检查所有人戴口罩(对应:环绕通知,在核心业务中执行);

管理员晚上关门前,统一安排所有人打扫卫生(对应:后置通知,在核心业务后执行)。

这样一来:

厨师、服务员、收银员 只需要专注做自己的核心工作(做菜、点餐、收钱),不用管那些通用规矩;

通用规矩 只需要交给管理员一个人管,以后要改规则,只需要跟管理员说一声就行;

核心工作和通用规矩 彻底分开,互不干扰。

二、Spring AOP 核心原理

Spring AOP 的底层实现有两种核心技术,都是动态代理,Spring 会自动选择合适的代理方式,无需开发者手动指定:

方式 1:JDK 动态代理(Spring 默认首选)

实现条件:被代理的目标类必须实现了接口

核心原理:基于接口生成「目标类的接口子类代理对象」,代理对象中封装了原目标对象的核心业务逻辑 + 切面的横切逻辑。

特点:JDK 原生支持,无需依赖第三方包,轻量高效。

示例:

步骤 1:定义业务接口(JDK 动态代理的前提)
java 复制代码
// 业务接口:对应餐厅的"核心工作规范"
public interface UserService {
    // 核心业务方法:对应"做菜/点餐"
    void addUser(String username);
}
步骤 2:实现目标类(被代理的真实业务类)
java 复制代码
// 目标类:实现接口,对应"厨师"(真正干活的人)
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        // 核心业务逻辑:对应"做菜"
        System.out.println("【核心业务】添加用户:" + username);
    }
}
步骤 3:手动实现 JDK 动态代理
java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 1. 定义InvocationHandler:相当于AOP的"切面",封装横切逻辑
class UserServiceInvocationHandler implements InvocationHandler {
    // 目标对象:真正干活的"厨师"
    private final Object target;

    // 构造方法:传入目标对象
    public UserServiceInvocationHandler(Object target) {
        this.target = target;
    }

    // 2. 核心方法:代理对象执行任何方法时,都会走这个方法(植入横切逻辑的关键)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // ====== 前置横切逻辑:对应AOP的@Before ======
        System.out.println("【JDK代理-前置】记录请求日志,方法名:" + method.getName() + ",参数:" + args[0]);
        
        // ====== 执行目标方法:让"厨师"干核心活 ======
        Object result = method.invoke(target, args);
        
        // ====== 后置横切逻辑:对应AOP的@After ======
        System.out.println("【JDK代理-后置】记录响应日志,方法执行完成");
        
        return result;
    }
}

// 测试类
public class JdkProxyDemo {
    public static void main(String[] args) {
        // 1. 创建目标对象(真正干活的"厨师")
        UserService target = new UserServiceImpl();
        
        // 2. 创建InvocationHandler(封装横切逻辑的"管理员")
        InvocationHandler handler = new UserServiceInvocationHandler(target);
        
        // 3. 生成JDK动态代理对象(相当于"带管理员监督的厨师")
        // 核心API:Proxy.newProxyInstance(类加载器, 目标接口数组, 处理器)
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 类加载器
                target.getClass().getInterfaces(),   // 目标类实现的接口(JDK代理的核心)
                handler                              // 横切逻辑处理器
        );
        
        // 4. 调用代理对象的方法(不是直接调用目标对象!)
        proxy.addUser("张三");
    }
}
结果
复制代码
【JDK代理-前置】记录请求日志,方法名:addUser,参数:张三
【核心业务】添加用户:张三
【JDK代理-后置】记录响应日志,方法执行完成

方式 2:CGLIB 动态代理(自动兜底方案)

实现条件:被代理的目标类没有实现任何接口

核心原理:通过字节码增强技术,生成「目标类的子类代理对象」,子类中重写目标类的方法,植入横切逻辑。

特点:无需实现接口,适配所有类,Spring 已内置 CGLIB,无需额外引入依赖。

示例:

前置准备

CGLIB 是第三方库,如果你用 Maven,需要先引入依赖(Spring 已内置,无需手动加):

xml 复制代码
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
步骤 1:定义目标类(无需实现任何接口)
java 复制代码
// 目标类:没有接口,对应"没有规范的临时工厨师"
public class OrderService {
    // 核心业务方法:对应"做菜"
    public void createOrder(String orderNo) {
        System.out.println("【核心业务】创建订单:" + orderNo);
    }
}
步骤 2:手动实现 CGLIB 动态代理
java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 1. 定义MethodInterceptor:相当于AOP的"切面",封装横切逻辑
class OrderServiceMethodInterceptor implements MethodInterceptor {
    // 目标对象:真正干活的"厨师"
    private final Object target;

    // 构造方法:传入目标对象
    public OrderServiceMethodInterceptor(Object target) {
        this.target = target;
    }

    // 2. 核心方法:代理对象执行任何方法时,都会走这个方法
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // ====== 前置横切逻辑:对应AOP的@Before ======
        System.out.println("【CGLIB代理-前置】记录请求日志,方法名:" + method.getName() + ",参数:" + args[0]);
        
        // ====== 执行目标方法:让"厨师"干核心活 ======
        Object result = methodProxy.invoke(target, args);
        
        // ====== 后置横切逻辑:对应AOP的@After ======
        System.out.println("【CGLIB代理-后置】记录响应日志,方法执行完成");
        
        return result;
    }
}

// 测试类
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 1. 创建目标对象(真正干活的"厨师")
        OrderService target = new OrderService();
        
        // 2. 创建Enhancer:CGLIB的核心类,用于生成代理子类
        Enhancer enhancer = new Enhancer();
        // 设置父类:CGLIB是生成目标类的子类(核心!)
        enhancer.setSuperclass(OrderService.class);
        // 设置回调:横切逻辑处理器
        enhancer.setCallback(new OrderServiceMethodInterceptor(target));
        
        // 3. 生成CGLIB代理对象(目标类的子类)
        OrderService proxy = (OrderService) enhancer.create();
        
        // 4. 调用代理对象的方法
        proxy.createOrder("ORDER_10086");
    }
}

执行结果

复制代码
【CGLIB代理-前置】记录请求日志,方法名:createOrder,参数:ORDER_10086
【核心业务】创建订单:ORDER_10086
【CGLIB代理-后置】记录响应日志,方法执行完成

核心结论

Spring AOP 是基于动态代理的运行时增强技术 ,它不会修改目标类的源码,所有的横切逻辑植入都是在程序运行阶段 完成,对目标类零侵入

三、Spring AOP 核心术语(重中之重)

Spring AOP 的所有配置和使用都基于这些术语,理解这些术语 = 学会一半 Spring AOP,所有术语都是成对出现、相互关联:

1. 切面(Aspect)

通俗理解:封装了「横切逻辑」的类(比如:日志切面类、事务切面类、权限切面类),这个类里写的就是所有要统一处理的非业务逻辑。

专业定义:横切逻辑的模块化封装,一个切面对应一类共性功能,是 AOP 的核心载体。

2. 连接点(JoinPoint)

通俗理解:程序执行过程中的「所有可增强的位置」,Spring AOP 中特指「目标类中所有的方法」(因为 Spring AOP 只支持方法级别的增强)。

专业定义:程序执行流中可以植入切面逻辑的所有点,Spring AOP 中仅支持方法连接点。

补充:并不是所有连接点都会被增强,只是「有机会被增强」的点。

3. 切入点(Pointcut)

通俗理解:连接点的「子集」 → 开发者明确指定要增强的那些方法(比如:指定 com.xxx.service 包下所有类的所有方法、指定所有以 add 开头的方法)。

专业定义:匹配连接点的规则,通过切入点表达式指定哪些连接点会被植入切面逻辑。

核心关系:切入点 ⊂ 连接点,连接点是「候选集」,切入点是「选中集」。

4. 通知(Advice)

通俗理解:切面类中真正执行的「具体逻辑代码」(比如:日志切面中打印日志的代码、事务切面中开启事务 / 提交事务的代码)。

专业定义:切面在特定连接点上执行的具体行为,是切面的核心逻辑体。

核心分类(Spring AOP 核心 5 种,必须记):Spring AOP 的核心精髓就在通知的时机控制,所有通知都作用在切入点方法上

前置通知 @Before:目标方法执行之前 执行通知逻辑(比如:参数校验、日志前置打印)

后置通知 @After:目标方法执行之后 执行通知逻辑,无论目标方法是否抛出异常都会执行(比如:释放资源、关闭连接)

返回通知 @AfterReturning:目标方法「正常执行完成并返回结果后」 执行,目标方法抛异常则不执行(比如:打印接口返回值、处理返回结果)

异常通知 @AfterThrowing:目标方法抛出异常时 执行,正常执行则不执行(比如:打印异常堆栈、记录异常日志、告警)

环绕通知 @Around:功能最强的通知,可以包裹目标方法的执行全过程,能在目标方法执行前、执行后、抛异常时都做处理,还能手动控制目标方法是否执行、手动修改返回值。

5. 目标对象(Target)

通俗理解:被代理、被增强的那个业务类对象(比如:UserService、OrderService),里面写的是核心业务逻辑。

专业定义:包含核心业务逻辑,被切面增强的原始对象。

6. 代理对象(Proxy)

通俗理解:Spring AOP 通过动态代理生成的新对象,这个对象里包含了「目标对象的业务逻辑」+「切面的横切逻辑」,项目中实际调用的是这个代理对象。

专业定义:目标对象被 AOP 增强后,Spring 容器生成的代理实例,对外暴露的是代理对象。

7. 织入(Weaving)

通俗理解:把「切面的通知逻辑」植入到「目标对象的切入点方法」中 的这个动作 / 过程。

专业定义:将切面逻辑与目标对象的业务逻辑结合,生成代理对象的过程。

补充:Spring AOP 的织入是在 运行期 完成的(动态代理的特性),这也是 Spring AOP 零侵入的核心原因。

补:

为了便于理解以上术语的定义,简单打个比方:

你要拍一部美食电影(对应 一个 Spring 项目),核心是「展示一道菜的制作过程」(对应核心业务逻辑),同时还要加「灯光、收音、字幕」这些通用效果(对应横切逻辑)。

切面(Aspect)= 摄制组

摄制组不参与做菜(核心业务),只负责加通用特效(横切逻辑),一个摄制组对应一类特效(比如灯光组就是 "灯光切面")。

连接点(JoinPoint)= 所有可拍镜头

洗菜、切菜、炒菜、装盘都是 "可拍镜头"(可增强方法),但不一定都要加特效。

切入点(Pointcut)= 导演指定的镜头

导演说 "只给炒菜和装盘加特效",这两个镜头就是切入点,是连接点的 "选中款"。

通知(Advice)= 摄制组的具体操作

@Before 前置通知 = 炒菜前先打光;

@AfterReturning 返回通知 = 菜炒好装盘后加字幕;

@AfterThrowing 异常通知 = 炒菜炒糊了(异常),立刻切故障画面;

@After 后置通知 = 不管菜炒没炒糊,最后都要关收音;

@Around 环绕通知 = 全程跟着厨师,从准备食材到装盘,全程把控特效时机。

目标对象(Target)= 厨师

厨师只负责做菜,不管灯光字幕,对应业务类只写核心逻辑。

代理对象(Proxy)= 最终电影画面

观众看到的不是厨师的原始做菜过程,而是加了灯光、字幕、音效的 "增强版画面",对应项目中调用的是代理对象。

织入(Weaving)= 后期合成

把灯光、字幕合成到原始做菜画面的过程,就是织入。Spring AOP 是运行期织入,相当于 "实时合成",不用提前修改原始画面(零侵入)。

总结一下就是:

拍电影就是:摄制组(切面) 按照导演要求(切入点),在指定镜头(连接点) 上做特效操作(通知),把特效合成到厨师做菜的原始画面(目标对象) 里,最终生成带特效的电影(代理对象),这个合成过程(织入) 就是 AOP 的核心逻辑。


四、Spring AOP 完整入门案例(注解式开发,最常用)

前置说明

Spring AOP 有两种开发方式:XML 配置式 和 注解式,SpringBoot 中几乎全部使用注解式开发(简洁高效),也是企业开发主流,下面基于 SpringBoot 实现完整案例,可直接复制运行。

步骤 1:引入核心依赖(Maven)

SpringBoot 中引入 AOP 启动器即可,自动集成所有依赖(无需手动引入 AspectJ、CGLIB)

xml 复制代码
<!-- Spring AOP 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

步骤 2:编写「目标对象」- 业务核心类(被增强的类)

模拟一个用户业务层,包含核心业务方法,这就是我们要增强的目标对象

java 复制代码
package com.example.demo.service;
import org.springframework.stereotype.Service;

// 目标对象:被增强的业务类
@Service
public class UserService {

    // 切入点1:普通业务方法
    public void addUser(String username) {
        System.out.println("【核心业务】执行添加用户逻辑,用户名:" + username);
    }

    // 切入点2:带返回值的业务方法
    public String getUserById(Integer id) {
        System.out.println("【核心业务】执行查询用户逻辑,用户ID:" + id);
        // 模拟正常返回
        return "用户-" + id;
    }

    // 切入点3:会抛出异常的业务方法
    public void deleteUser(Integer id) {
        System.out.println("【核心业务】执行删除用户逻辑,用户ID:" + id);
        // 模拟业务异常
        throw new RuntimeException("删除用户失败,ID不存在:" + id);
    }
}

步骤 3:编写「切面类」- 封装横切逻辑(核心)

这是 Spring AOP 的核心类,通过注解声明切面、切入点、通知:

@Aspect:标注当前类是「切面类」,Spring 会识别并解析该类中的切面逻辑

@Component:必须加!切面类也是 Spring 组件,需要被 IOC 容器扫描并管理

@Pointcut:定义「切入点表达式」,抽取公共的切入点规则,供所有通知复用

java 复制代码
package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

// 1. 声明这是一个切面类
@Aspect
// 2. 声明这是一个Spring组件,交给IOC容器管理
@Component
public class LogAspect {

    // ========== 定义切入点:指定要增强的方法 ==========
    // 切入点表达式:匹配 com.example.demo.service 包下所有类的所有方法
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void pointcut() {}

    // ========== 1. 前置通知:目标方法执行前执行 ==========
    @Before("pointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        // joinPoint 可以获取目标方法的信息:类名、方法名、参数等
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("【前置通知】执行前 → 类名:" + className + ",方法名:" + methodName);
    }

    // ========== 2. 后置通知:目标方法执行后执行(无论是否异常) ==========
    @After("pointcut()")
    public void afterAdvice() {
        System.out.println("【后置通知】执行后 → 无论方法是否异常,我都会执行\n");
    }

    // ========== 3. 返回通知:目标方法正常返回后执行 ==========
    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturningAdvice(Object result) {
        System.out.println("【返回通知】正常返回 → 方法返回值:" + result);
    }

    // ========== 4. 异常通知:目标方法抛出异常时执行 ==========
    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterThrowingAdvice(Throwable e) {
        System.out.println("【异常通知】抛出异常 → 异常信息:" + e.getMessage());
    }

    // ========== 5. 环绕通知:包裹目标方法全程(单独演示,注释上面4个通知看效果更佳) ==========
    // @Around("pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        // 前置逻辑:目标方法执行前
        System.out.println("【环绕前置】目标方法执行前的逻辑");
        // 手动执行目标方法,获取返回值(如果不调用,目标方法不会执行!)
        Object result = joinPoint.proceed();
        // 后置逻辑:目标方法执行后
        System.out.println("【环绕后置】目标方法执行后的逻辑,返回值:" + result);
        // 返回结果
        return result;
    }
}

步骤 4:编写测试类,执行测试

java 复制代码
package com.example.demo;

import com.example.demo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class AopTest {

    // 注意:这里注入的是 代理对象,不是原始的UserService对象
    @Autowired
    private UserService userService;

    @Test
    public void testAop() {
        // 测试1:执行无返回值、无异常的方法
        userService.addUser("张三");

        // 测试2:执行有返回值、无异常的方法
        userService.getUserById(1001);

        // 测试3:执行会抛出异常的方法
        try {
            userService.deleteUser(999);
        } catch (Exception e) {
            // 捕获异常,不影响测试
        }
    }
}

执行结果

复制代码
【前置通知】执行前 → 类名:UserService,方法名:addUser
【核心业务】执行添加用户逻辑,用户名:张三
【后置通知】执行后 → 无论方法是否异常,我都会执行

【前置通知】执行前 → 类名:UserService,方法名:getUserById
【核心业务】执行查询用户逻辑,用户ID:1001
【返回通知】正常返回 → 方法返回值:用户-1001
【后置通知】执行后 → 无论方法是否异常,我都会执行

【前置通知】执行前 → 类名:UserService,方法名:deleteUser
【核心业务】执行删除用户逻辑,用户ID:999
【异常通知】抛出异常 → 异常信息:删除用户失败,ID不存在:999
【后置通知】执行后 → 无论方法是否异常,我都会执行

核心结论:

  1. 前置通知一定在目标方法前执行;
  2. 返回通知 只在方法正常返回时执行,异常通知 只在方法抛异常时执行,二者互斥;
  3. 后置通知 是「最终通知」,无论成功还是失败,必然执行。

五、Spring AOP 支持的切入点表达式(常用)

上面案例中用到的 execution() 是最常用的切入点表达式,Spring AOP 基于 AspectJ 提供了多种表达式,这里列出开发中最常用的 3 种,足够应对大部分的场景:

1. execution (表达式) 【最常用】

匹配方法的执行,语法格式:

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

通配符说明:

* :匹配任意内容(任意返回值、任意类、任意方法、任意单个参数)

..:匹配任意多个参数,或任意子包

常用示例:

execution(* com.example.demo.service.*.*(..)) → 匹配 service 包下所有类的所有方法

execution(* com.example.demo.service.UserService.*(String)) → 匹配 UserService 中参数为 String 的所有方法

execution(public String com.example.demo.service.*.get*(..)) → 匹配 service 包下所有以 get 开头、返回值为 String 的公共方法

2. @annotation (注解全类名)

匹配被指定注解标注的所有方法,非常灵活,比如自定义一个 @Log 注解,只要方法上加了这个注解,就会被增强:

java 复制代码
// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}

// 切入点表达式:匹配所有被@Log注解标注的方法
@Pointcut("@annotation(com.example.demo.annotation.Log)")
public void annotationPointcut() {}

3. within (包名 / 类名)

匹配指定包下的所有类的所有方法,粒度比 execution 粗,比如:

java 复制代码
@Pointcut("within(com.example.demo.service.*)")

六、Spring AOP 的优缺点(面试高频)

优点(核心优势,为什么必须用)

  1. 无侵入解耦:切面逻辑与业务逻辑完全分离,业务代码无任何切面相关代码,零侵入;
  2. 代码复用:横切逻辑只需编写一次,全局生效,彻底解决代码冗余问题;
  3. 维护便捷:修改横切逻辑只需修改切面类,无需修改所有业务方法,降低维护成本;
  4. 专注业务:开发人员只需关注核心业务逻辑,无需关心日志、事务等通用逻辑;
  5. 灵活可控:通过切入点表达式精准控制要增强的方法,粒度可控。

缺点(局限性,了解即可)

仅支持方法级增强:Spring AOP 是方法级别的 AOP,只能对「方法」进行增强,无法对类、属性、构造方法等进行增强(如果需要更细粒度的增强,可使用 AspectJ);

基于动态代理的局限:只能增强 Spring IOC 容器中的受管对象(被 @Component/@Service 等注解的对象),无法增强普通 new 出来的对象;

性能轻微损耗:动态代理是运行期生成对象,会有极轻微的性能损耗,但在绝大多数业务场景下可以忽略不计(性能损耗远小于开发效率的提升)。


七、总结

  1. Spring AOP 是面向切面编程的实现,与 OOP 互补,核心解决「横切逻辑与业务逻辑解耦」的问题;

  2. 底层是动态代理:有接口用 JDK 动态代理,无接口用 CGLIB 动态代理,Spring 自动选择;

  3. 核心 7 个术语:切面 (Aspect)、连接点 (JoinPoint)、切入点 (Pointcut)、通知 (Advice)、目标对象 (Target)、代理对象 (Proxy)、织入 (Weaving)

  4. 核心 5 种通知:前置 @Before、后置 @After、返回 @AfterReturning、异常 @AfterThrowing、环绕 @Around

    补:

    执行顺序:分为两种情况:

    一种是无异常的场景:

    @Around(前置逻辑)@Before → 目标方法 → @AfterReturning@After →@Around(后置逻辑)

    另一种是有异常的场景:

    @Around(前置逻辑)@Before → 目标方法(抛异常) → @AfterThrowing@After@Around(异常捕获逻辑)

  5. 主流开发方式是注解式 ,核心注解:@Aspect + @Component + @Pointcut + 通知注解;

  6. 核心价值:解耦、复用、易维护、专注业务,是企业级开发中必备的核心技术。

相关推荐
Jing_jing_X13 小时前
AI分析不同阶层思维 二:Spring 的事务在什么情况下会失效?
java·spring·架构·提升·薪资
元Y亨H15 小时前
Nacos - 服务发现
java·微服务
麦聪聊数据15 小时前
MySQL并发与锁:从“防止超卖”到排查“死锁”
数据库·sql·mysql
微露清风15 小时前
系统性学习C++-第十八讲-封装红黑树实现myset与mymap
java·c++·学习
dasi022715 小时前
Java趣闻
java
AC赳赳老秦16 小时前
DeepSeek 私有化部署避坑指南:敏感数据本地化处理与合规性检测详解
大数据·开发语言·数据库·人工智能·自动化·php·deepseek
阿波罗尼亚16 小时前
Tcp SSE Utils
android·java·tcp/ip
susu108301891116 小时前
springboot3.5.8整合minio8.5.9
java·springboot
不知道累,只知道类16 小时前
深入理解 Java 虚拟线程 (Project Loom)
java·开发语言