在 Spring 开发中我们经常使用Transactional、日志切面等功能但很多人并不清楚它们是如何工作的。本质上这些能力都建立在Spring AOP之上。本文面向准备后端校招面试的同学聚焦以下核心问题AOP 的设计初衷是什么为什么不能只用工具类Spring AOP 如何通过动态代理实现“织入”代理对象在 Bean 生命周期中何时创建为什么this.method()调用会导致 AOP 或事务失效不展开 AspectJ 或字节码生成细节只讲清Spring AOP 在面试中最常被考察的核心机制。一、什么是 AOPAOPAspect Oriented Programming即面向切面编程。在实际开发中一个业务方法往往不仅包含核心逻辑还会混入一些通用逻辑例如public void createOrder() { // 记录日志 log.info(create order); // 权限校验 checkPermission(); // 事务控制 beginTransaction(); orderService.save(); commitTransaction(); }这些逻辑具有共同特点与核心业务无关在多个方法中重复出现AOP 的目标就是将这类横切关注点Cross-Cutting Concerns从业务代码中抽离形成统一的切面Aspect。常见应用场景包括日志记录事务管理权限校验性能监控二、Spring AOP 的核心概念理解 Spring AOP 需要掌握几个基本术语概念说明Aspect切面封装横切逻辑的类Advice通知具体的增强行为Pointcut切点定义哪些方法会被拦截JoinPoint连接点程序执行过程中的某个点如方法调用Proxy代理对象用于拦截方法调用简单流程方法调用 ↓ 切点匹配 ↓ 代理对象拦截 ↓ 执行通知Advice三、Spring AOP 的基本使用在 Spring 中使用 AOP 非常直接。例如定义一个日志切面Aspect Component public class LogAspect { Before(execution(* com.demo.service.*.*(..))) public void before() { System.out.println(before method); } }当调用orderService.createOrder()时Spring 会自动在方法执行前插入日志逻辑。但关键问题是Spring 是如何把切面“织入”到业务方法中的答案是代理对象。四、Spring AOP 的实现机制Spring AOP 的底层基于动态代理主要有两种方式代理方式适用条件JDK 动态代理目标类实现了接口CGLIB 代理目标类没有接口Spring 的默认策略是优先使用 JDK 动态代理若无接口则使用 CGLIB。代理对象的调用结构如下客户端 ↓ 代理对象Proxy ↓ 拦截器链Interceptor Chain ↓ 目标对象Target所有方法调用实际上都经过代理对象从而有机会执行切面逻辑。五、代理对象是什么时候创建的这是理解 Spring AOP 的关键。很多人误以为代理在 Bean 实例化时就生成了实际上代理是在 Bean 初始化完成之后通过BeanPostProcessor创建的。具体生命周期流程1. 实例化 Bean 2. 依赖注入populate 3. 初始化InitializingBean / init-method 4. BeanPostProcessor.postProcessAfterInitialization() 5. → 此时判断是否需要创建代理核心实现类是AbstractAutoProxyCreator其postProcessAfterInitialization()方法中调用wrapIfNecessary(bean, beanName)若当前 Bean 匹配任意切点则为其创建代理对象。六、AOP 方法调用的执行流程当客户端调用方法时实际执行路径为客户端调用 ↓ 代理对象.invoke() ↓ ReflectiveMethodInvocation.proceed() ↓ 遍历拦截器链MethodInterceptor ↓ 依次执行 Before、Around 等 Advice ↓ 最终调用目标方法Spring 将多个通知封装为一个拦截器链Interceptor Chain通过责任链模式依次执行。七、为什么内部调用this.method()会导致 AOP 失效这是校招面试高频问题。Service public class OrderService { public void methodA() { this.methodB(); // 内部调用 } Transactional public void methodB() { // 数据库操作 } }此时Transactional不会生效。原因在于Spring AOP 是基于代理实现的只有通过代理对象的方法调用才会触发切面逻辑。而this.methodB()是直接调用目标对象的方法绕过了代理因此事务或其他 AOP 逻辑不会被织入。解决思路包括将methodB拆到另一个 Service 中通过自注入获取代理实例Autowired private OrderService self;八、Spring AOP 整体流程回顾将上述环节串联起来1. 容器启动扫描 Aspect 注解 2. 注册 AbstractAutoProxyCreatorBeanPostProcessor 3. Bean 初始化完成后判断是否匹配切点 4. 若匹配创建代理对象JDK / CGLIB 5. 后续方法调用通过代理进入拦截器链 6. 执行通知逻辑再调用目标方法九、面试回答参考如果面试官问“Spring AOP 是如何实现的”可参考如下回答Spring AOP 基于动态代理实现。在 Bean 初始化完成后Spring 通过BeanPostProcessor具体是AbstractAutoProxyCreator判断该 Bean 是否匹配切面定义。如果匹配则为其创建代理对象有接口用 JDK 代理无接口用 CGLIB。后续对该 Bean 的方法调用实际是调用代理对象的方法代理会将通知封装为拦截器链在目标方法执行前后插入增强逻辑。需要注意的是只有通过代理对象的调用才会触发 AOP内部调用如this.xxx()会绕过代理导致切面失效。十、总结Spring AOP 的核心机制可归纳为三点基于动态代理JDK / CGLIB实现运行时织入代理对象在BeanPostProcessor.postProcessAfterInitialization()阶段创建只有通过代理对象的方法调用才会进入拦截器链并执行切面逻辑。理解这三点就能解释包括Transactional失效在内的大多数 AOP 相关面试问题。欢迎在评论区交流你在 AOP 或 Spring 面试中遇到的问题。如果你觉得本文帮你理清了原理脉络欢迎点赞或收藏方便面试前快速回顾。