ai小店助手带你读懂AOP——Spring核心技术深度剖析

小编头像

小编

管理员

发布于:2026年04月28日

6 阅读 · 0 评论

北京时间2026年4月10日发布 | 阅读时长约8分钟

在Spring技术体系中,面向切面编程是与IoC并称的双核之一,堪称每一位Java开发者的必修课-1。然而很多开发者在实际工作中常陷入这样的困境:会用@Transactional做事务管理,会写@Around记录方法耗时,却说不清AOP底层的动态代理到底是怎么工作的;知道JDK动态代理和CGLIB两种方式,却讲不透它们的本质区别——面试被追问时常常语塞。本文将以ai小店助手为线索,从“为什么需要AOP”出发,带你彻底搞懂核心概念、理清代码逻辑、掌握底层原理、背熟高频考点。


一、痛点切入:为什么需要AOP?

先看一段传统代码。假设你有一个用户登录和下单的业务方法:

java
复制
下载
public class UserService {
    public void login(String username, String password) {
        // 日志记录
        System.out.println("[LOG] 开始登录,用户名:" + username);
        // 权限校验
        System.out.println("[AUTH] 校验用户权限");
        long start = System.currentTimeMillis();
        // 核心业务逻辑
        System.out.println("执行登录逻辑...");
        long end = System.currentTimeMillis();
        System.out.println("[MONITOR] 执行耗时:" + (end - start) + "ms");
    }

    public void placeOrder(Long productId) {
        System.out.println("[LOG] 开始下单,商品ID:" + productId);
        System.out.println("[AUTH] 校验用户权限");
        long start = System.currentTimeMillis();
        System.out.println("执行下单逻辑...");
        long end = System.currentTimeMillis();
        System.out.println("[MONITOR] 执行耗时:" + (end - start) + "ms");
    }
}

这种做法的缺陷十分明显:

  • 代码重复严重:每个方法都要写一遍日志、权限、监控逻辑-1

  • 耦合度高:业务代码与非功能性代码混杂在一起

  • 维护困难:修改日志格式需要改动所有业务方法-8

AOP正是为解决这些痛点而生的编程范式——面向切面编程(Aspect Oriented Programming)-1。它将日志、事务、权限等横切逻辑从业务代码中抽离出来,形成独立的“切面”模块,在不修改原有业务代码的前提下,自动织入到目标方法中执行-1


二、核心概念讲解:AOP(Aspect Oriented Programming)

定义:AOP全称 Aspect Oriented Programming,中文译为面向切面编程。它是一种编程范式,通过预编译方式和运行期动态代理实现程序功能的统一维护,将横跨多个业务模块的通用功能(如日志记录、事务管理、权限验证等)从业务逻辑中分离出来,模块化处理--21

类比理解:如果把整个应用程序想象成一个大型流水线生产线,每个业务方法就像一台独立的加工机器。传统OOP的做法是给每台机器都单独加装“安全护栏”、“计时器”、“录像设备”,改造起来相当麻烦。AOP的做法则是在流水线之外设置一个统一的“拦截关卡”,任何机器工作前、工作中、工作后都能自动触发相应处理,所有“护栏”只需在一个地方配置,即可对多台机器生效。

核心价值:解决了OOP难以处理的横切关注点问题——那些散布在多处、与核心业务逻辑关系不大的公共功能,实现横向抽取共性功能,降低模块间耦合度-21


三、关联概念讲解:IoC与DI

如果说AOP是Spring的“左膀”,那IoC就是“右臂”。理解AOP离不开对IoC的认知。

IoC(Inversion of Control,控制反转) 是一种设计思想,它颠覆了传统编程中由调用者主动创建依赖对象的控制流程——控制权从调用者转移到外部容器(Spring容器)-39

DI(Dependency Injection,依赖注入) 是IoC的一种具体实现方式,即由容器在运行时将依赖对象“注入”到组件中,而非组件自己去创建依赖-39-40

关系总结:IoC是设计思想,DI是实现手段。IoC解决了“谁控制谁”的问题,DI解决了“如何传递依赖”的问题。

java
复制
下载
// 传统方式:主动创建依赖,紧耦合
public class UserService {
    private UserDao userDao = new UserDaoImpl();  // 硬编码
}

// IoC + DI 方式:被动接收依赖,松耦合
@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 由容器注入
}

四、概念关系与区别总结

对比维度AOP(面向切面编程)IoC(控制反转)
本质编程范式 / 技术思想设计原则 / 架构思想
解决的问题横切逻辑的复用与解耦对象依赖关系的解耦
实现方式动态代理(JDK / CGLIB)依赖注入(DI)
核心模块切面、通知、切点Bean容器、ApplicationContext

一句话概括:IoC解决的是对象“怎么创建、依赖谁”的问题,AOP解决的是方法“什么时候做额外的事”的问题。二者共同构成了Spring框架的两大基石-53


五、代码示例:Spring Boot中的AOP实战

下面通过一个完整的例子演示如何用Spring AOP实现方法执行耗时统计

第一步:添加依赖

xml
复制
下载
运行
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

第二步:编写业务方法

java
复制
下载
@Service
public class DeptServiceImpl implements DeptService {
    public List<Dept> list() {
        // 模拟业务逻辑
        System.out.println("执行部门列表查询...");
        return new ArrayList<>();
    }
}

第三步:编写切面类

java
复制
下载
@Component
@Aspect  // 标记为切面类
@Slf4j
public class TimeAspect {

    // 环绕通知:在目标方法前后执行增强逻辑
    @Around("execution( com.example.service.impl.DeptServiceImpl.list())")
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long begin = System.currentTimeMillis();
        Object result = joinPoint.proceed();  // 调用原始业务方法
        long end = System.currentTimeMillis();
        log.info("执行耗时 : {} ms", (end - begin));
        return result;
    }
}

关键步骤说明

  1. @Aspect:标记该类为切面类-1

  2. @Component:将该类交给Spring容器管理-1

  3. @Around:环绕通知注解,括号内写切入点表达式,指定拦截哪些方法-1

  4. ProceedingJoinPoint.proceed():调用原始业务方法,环绕通知必须手动调用-1

执行流程:当调用DeptServiceImpl.list()时,Spring AOP生成的代理对象会先执行recordTime方法 → 执行proceed()调用原始业务 → 执行耗时计算并返回结果-1


六、底层原理:动态代理机制

Spring AOP的底层核心是动态代理技术。当业务方法被调用时,实际调用的并非原始对象,而是一个代理对象,由代理对象在方法调用前后插入切面逻辑-9

Spring AOP根据目标类的特性智能选择两种代理方式-8

对比维度JDK动态代理CGLIB动态代理
实现原理基于反射,动态生成实现了接口的代理类基于字节码技术,动态生成目标类的子类
必要条件目标类必须实现至少一个接口目标类不能是final类,方法不能是final
创建性能创建速度快创建速度慢(约JDK的8倍)-
执行性能执行速度一般执行速度快(约JDK的10倍)-
适用场景接口代理场景类代理场景-16

决策逻辑:Spring通过DefaultAopProxyFactory自动判断——若目标类实现了接口,默认使用JDK动态代理;若没有实现接口或配置了proxyTargetClass=true,则使用CGLIB-8-9


七、高频面试题与参考答案

面试题1:什么是AOP?它的核心概念有哪些?

参考答案:AOP全称Aspect Oriented Programming,面向切面编程,是Spring框架的两大核心思想之一。核心概念包括:切面(Aspect)——封装横切逻辑的模块;连接点(Join Point)——可被增强的方法;切点(Pointcut)——匹配连接点的规则;通知(Advice)——在切点处执行的增强逻辑,分为前置、后置、返回、异常、环绕五种;织入(Weaving)——将切面应用到目标对象的过程-1

面试题2:Spring AOP的底层原理是什么?JDK动态代理和CGLIB有什么区别?

参考答案:Spring AOP底层基于动态代理技术,为目标对象创建代理对象,在方法调用前后插入增强逻辑。区别在于:JDK动态代理要求目标类实现接口,通过反射生成代理类;CGLIB通过字节码技术生成目标类的子类,不要求实现接口。Spring默认优先使用JDK代理,无接口时自动切换到CGLIB-22

面试题3:AOP的五大通知类型分别是什么?它们的执行顺序是怎样的?

参考答案:五大通知类型——前置通知(@Before,目标方法执行前执行);后置通知(@After,无论是否异常都会执行);返回通知(@AfterReturning,正常返回后执行);异常通知(@AfterThrowing,抛出异常后执行);环绕通知(@Around,包裹整个方法,前后都可执行)-1。正常执行顺序为:@Before → 目标方法 → @AfterReturning → @After;发生异常时:@Before → 目标方法 → @AfterThrowing → @After。

面试题4:AOP为什么对private方法不生效?

参考答案:Spring AOP基于动态代理实现,代理对象只能拦截public方法的调用。private方法属于类内部调用,不经过代理对象,因此无法被拦截和增强-25

面试题5:@Around环绕通知为什么要手动调用proceed()?

参考答案:proceed()是ProceedingJoinPoint接口的核心方法,用于调用原始目标方法。环绕通知需要程序员手动控制“何时执行原始方法”——可以决定在增强逻辑之前调用、之后调用、甚至不调用(拦截方法执行),从而实现更灵活的控制-1


八、结尾总结

本文系统讲解了Spring AOP的完整知识链路:

  • 问题来源:传统OOP在处理日志、事务等横切逻辑时存在代码重复、耦合度高、维护困难等痛点

  • 核心概念:切面、连接点、切点、通知、织入——理解了这五个术语就理解了AOP的全部骨架

  • 代码实现:通过@Aspect + @Around注解,配合切入点表达式,轻松实现耗时统计等功能

  • 底层原理:JDK动态代理(面向接口)与CGLIB动态代理(面向类)的机制差异与选择逻辑

  • 面试考点:五大通知类型、代理机制区别、private方法失效原因、proceed()调用必要性

重点提示:很多面试者会在“自调用失效”问题上栽跟头——同一个Bean内部通过this调用另一个被AOP增强的方法时,由于调用未经过代理对象,切面逻辑不会执行。解决方式是通过ApplicationContext.getBean()获取代理对象后再调用,或注入自身(@Autowired当前类)-25

下期预告:我们将深入剖析Spring IoC容器的初始化流程与Bean生命周期,敬请期待。

标签:

相关阅读