Spring AOP 原理详解:从动态代理到切面注解的源码级剖析
~~ Talk is cheap. Show me the code. ~~本文基于 Spring Framework 5.x / Spring Boot 2.x 版本讲解 Spring AOP 的核心机制与实现原理。一、引言AOPAspect-Oriented Programming面向切面编程是 Spring 框架的核心能力之一它通过将横切关注点如日志记录、事务管理、权限校验、性能监控等从业务逻辑中分离出来显著降低了代码耦合度。在技术面试中Spring AOP 的实现原理、动态代理机制以及事务管理背后的 AOP 支撑都是高频考察点。然而多数人对 AOP 的理解停留在加个Aspect注解就能切面增强的层面对其底层的代理机制、通知类型、切点表达式以及执行顺序缺乏系统认知。本文将从代理模式出发沿 Spring 源码深入剖析 AOP 的完整实现链路帮助读者建立对 AOP 机制的完整认知。二、AOP 的核心概念在深入源码之前首先明确 AOP 的几个核心术语它们是理解后续内容的基础术语说明切面Aspect横切关注点的模块化通常是一个标注了Aspect的类连接点JoinPoint程序执行过程中的某个位置如方法调用、异常抛出切点Pointcut匹配连接点的谓词表达式决定通知作用的位置通知Advice切面在特定切点执行的动作如Before、After目标对象Target被代理的原始对象代理Proxy将通知应用到目标对象后生成的代理对象织入Weaving将切面应用到目标对象并创建代理对象的过程其中通知类型是理解 AOP 执行顺序的关键Spring AOP 支持以下五种通知通知类型注解执行时机前置通知Before目标方法执行之前后置通知After目标方法执行之后无论是否异常返回通知AfterReturning目标方法正常返回之后异常通知AfterThrowing目标方法抛出异常之后环绕通知Around包裹目标方法可控制是否执行目标方法三、AOP 的底层基石代理模式Spring AOP 的实现基础是代理模式Proxy Pattern其核心思想是不直接调用目标对象而是通过代理对象间接调用从而在调用前后插入额外的处理逻辑。Spring AOP 采用两种动态代理技术根据目标对象的类型自动选择1. JDK 动态代理基于接口实现要求目标对象必须实现至少一个接口。JDK 动态代理通过java.lang.reflect.Proxy与InvocationHandler实现public class JdkProxyFactory { public static Object createProxy(Object target, InvocationHandler handler) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); } }2. CGLIB 代理基于继承实现通过字节码技术为目标对象生成子类适用于未实现接口的目标对象。CGLIB 通过Enhancer与MethodInterceptor实现public class CglibProxyFactory { public static Object createProxy(Object target, MethodInterceptor interceptor) { Enhancer enhancer new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(interceptor); return enhancer.create(); } }两者的选择由DefaultAopProxyFactory决定其核心逻辑如下public class DefaultAopProxyFactory implements AopProxyFactory { Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class? targetClass config.getTargetClass(); if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } }由此可以得出选择规则默认使用 JDK 动态代理当目标对象未实现接口或显式配置proxy-target-classtrueSpring Boot 中为spring.aop.proxy-target-class时使用 CGLIB 代理。四、切面注解的声明Aspect 与 EnableAspectJAutoProxy使用注解方式定义切面时需要两个关键注解Configuration EnableAspectJAutoProxy public class AppConfig { // ... }Aspect Component public class LogAspect { Pointcut(execution(* com.example.service.*.*(..))) public void serviceLayer() {} Before(serviceLayer()) public void beforeAdvice(JoinPoint joinPoint) { System.out.println(前置通知 joinPoint.getSignature().getName()); } }其中EnableAspectJAutoProxy是开启 AOP 代理的关键开关其源码如下Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Import(AspectJAutoProxyRegistrar.class) public interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }该注解通过Import引入AspectJAutoProxyRegistrar后者实现了ImportBeanDefinitionRegistrar接口在容器启动时向 Bean 工厂注册一个AnnotationAwareAspectJAutoProxyCreator。class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); // ... } }AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor后置处理器它是 AOP 代理生成的真正执行者。五、代理创建的时机BeanPostProcessor 后置处理AnnotationAwareAspectJAutoProxyCreator的继承体系如下BeanPostProcessor └── InstantiationAwareBeanPostProcessor └── SmartInstantiationAwareBeanPostProcessor └── AbstractAutoProxyCreator └── AspectJAwareAdvisorAutoProxyCreator └── AnnotationAwareAspectJAutoProxyCreator其核心逻辑位于AbstractAutoProxyCreator的postProcessAfterInitialization()方法Override public Object postProcessAfterInitialization(Nullable Object bean, String beanName) { if (bean ! null) { Object cacheKey getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) ! bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }该方法在每个 Bean 初始化完成后被调用通过wrapIfNecessary()判断该 Bean 是否需要被代理核心判断逻辑如下protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 1. 获取该 Bean 匹配的所有通知Advisor Object[] specificInterceptors getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors ! DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 2. 创建代理对象 Object proxy createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }至此AOP 的代理生成时机已经清晰Spring 在 Bean 初始化完成后通过后置处理器判断该 Bean 是否匹配任何切点若匹配则为其创建代理对象替换原始 Bean。六、通知的执行拦截器链与责任链模式当代理对象的方法被调用时会进入代理的拦截逻辑。以 JDK 动态代理为例核心在JdkDynamicAopProxy的invoke()方法Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 获取当前方法的拦截器链 ListObject chain this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { // 无拦截器直接反射调用目标方法 return AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { // 创建方法调用对象责任链式执行 MethodInvocation invocation new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); return invocation.proceed(); } }拦截器链的执行采用责任链模式核心在ReflectiveMethodInvocation.proceed()方法Override public Object proceed() throws Throwable { // 拦截器执行完毕调用目标方法 if (this.currentInterceptorIndex this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // 动态匹配判断是否匹配当前方法 InterceptorAndDynamicMethodMatcher dm (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { return proceed(); } } else { // 静态匹配直接执行拦截器 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }该机制保证了通知的执行顺序Around前置逻辑 →Before→ 目标方法 →AfterReturning/AfterThrowing→After→Around后置逻辑。七、事务管理的 AOP 实现事务管理是 Spring AOP 最典型的应用场景。Transactional注解的背后正是通过 AOP 机制实现的事务增强。Spring 的事务管理核心拦截器为TransactionInterceptor其继承自TransactionAspectSupport实现了MethodInterceptor接口public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor { Override public Object invoke(MethodInvocation invocation) throws Throwable { Class? targetClass invocation.getThis() ! null ? AopUtils.getTargetClass(invocation.getThis()) : null; return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() { Override public Object proceedWithInvocation() throws Throwable { return invocation.proceed(); } }); } }invokeWithinTransaction()方法的核心逻辑是获取事务管理器 → 开启事务 → 执行目标方法 → 根据执行结果提交或回滚事务。当使用Transactional注解时Spring 通过TransactionAttributeSource读取事务属性并结合PlatformTransactionManager完成事务的开启、提交与回滚。八、实践基于注解的事务与日志切面本节通过一个完整示例演示 AOP 在实际工程中的典型应用。示例一事务管理Service public class OrderService { Autowired private OrderMapper orderMapper; Transactional(rollbackFor Exception.class) public void createOrder(Order order) { orderMapper.insert(order); // 模拟异常触发事务回滚 throw new RuntimeException(下单失败); } }示例二日志记录切面Aspect Component public class LogAspect { private static final Logger logger LoggerFactory.getLogger(LogAspect.class); Pointcut(execution(* com.example.service.*.*(..))) public void serviceLayer() {} Around(serviceLayer()) public Object around(ProceedingJoinPoint joinPoint) throws Throwable { long start System.currentTimeMillis(); String method joinPoint.getSignature().toShortString(); try { Object result joinPoint.proceed(); logger.info(方法 {} 执行成功耗时 {} ms, method, System.currentTimeMillis() - start); return result; } catch (Throwable e) { logger.error(方法 {} 执行异常, method, e); throw e; } } }九、原理总结Spring AOP 的完整实现链路可归纳为以下四个阶段1. 代理开关注册EnableAspectJAutoProxy通过Import引入AspectJAutoProxyRegistrar后者向容器注册AnnotationAwareAspectJAutoProxyCreator这一后置处理器。2. 代理对象生成Bean 初始化完成后后置处理器调用wrapIfNecessary()判断该 Bean 是否匹配任何切点若匹配则依据目标对象是否实现接口选择 JDK 动态代理或 CGLIB 代理创建代理对象。3. 拦截器链执行代理对象的方法被调用时通知被组织为拦截器链以责任链模式依次执行Around前置逻辑、Before、目标方法、AfterReturning、After等增强逻辑。4. 事务场景应用Spring 的事务管理正是基于上述机制由TransactionInterceptor在目标方法执行前后实现事务的开启、提交与回滚。十、结语AOP 是 Spring 框架将横切关注点模块化的核心手段。理解 AOP 的关键在于两点代理模式决定了增强逻辑如何被织入目标对象。责任链模式决定了多个通知如何按照既定顺序协同执行。掌握这两点即可从根本上理解 Spring AOP 的实现原理。在此基础上无论是应对技术面试还是在工程实践中运用事务管理、日志监控等横切能力都能够更加游刃有余。

相关新闻

Halcon 定位技术(Positioning)详解与算子说明

Halcon 定位技术(Positioning)详解与算子说明

Halcon 定位技术(Positioning)详解与算子说明 参考来源:Halcon 17.12 Operator Reference — Matching、2D Metrology、XLD Contours 章节 适用版本:Halcon 17.12(主基准)/ 20.11 / 22.11 / 24.05 官方示例路径:Applications/Matching/、Applications/Measuring-2D/、ex…

2026/10/15 3:59:45 阅读更多 →
HydroShear:解决剪切形变缺失,视触觉实现机器人零样本虚实迁移

HydroShear:解决剪切形变缺失,视触觉实现机器人零样本虚实迁移

当机器人学会「搓」:一张凝胶膜把触觉策略零样本搬进现实;把粘弹性算到像素里:SDF 剪切场,让 GelSight Mini 成为 RL 的「真手指」 把一根销钉塞进紧孔,人几乎不靠看,靠指腹那一点"还粘着 / 已经在滑"。 机…

2026/10/15 3:59:45 阅读更多 →
【python】函数二

【python】函数二

变量作用域变量作用域:变量生效的范围,分为局部变量和全局变量局部变量定义在函数内部,只能在函数里面使用,函数执行结束后,局部变量就销毁def testA():a10print(a)testA() # print(a) # 编译报错 局部变量&#xff0…

2026/10/15 3:59:45 阅读更多 →

最新新闻

基于AT89C51的DS18B20多点温度检测系统设计

基于AT89C51的DS18B20多点温度检测系统设计

简介:面向单片机课程设计与毕业设计场景,这份基于AT89C51的多点温度检测系统设计资料,完整解决了双点温度采集、LCD显示与系统搭建等问题。方案选用Dallas DS18B20单总线数字温度传感器,结合AT89C51单片机进行数据采集&#xff0c…

2026/10/15 4:47:54 阅读更多 →
PyFlink 1.11.0 UDF实战:标量、聚合、表函数选型与避坑指南

PyFlink 1.11.0 UDF实战:标量、聚合、表函数选型与避坑指南

1. 为什么要在 PyFlink 里折腾 UDFPyFlink 从 1.11.0 版本开始,对 Python 自定义函数(UDF)的支持才算真正进入可用阶段。在此之前,想在 Flink 作业里塞一段 Python 逻辑,要么绕道 Java 侧注册,要么干脆放弃…

2026/10/15 4:47:54 阅读更多 →
RS485通讯电路设计:光耦隔离六种典型方案对比与高速可靠传输选型指南

RS485通讯电路设计:光耦隔离六种典型方案对比与高速可靠传输选型指南

简介:RS485是工业现场最常用的串行通讯方式之一,但远距离传输中光耦工作区设置、整形电路阈值、驱动输出方式等因素,直接影响通讯速率与波形稳定性。这份资源以文档形式整理分析了五种典型的RS485通讯接线方案,逐一对电路结构、理…

2026/10/15 4:47:54 阅读更多 →
RTL8370N数据手册实战:寄存器、SMI与交换芯片调试要诀

RTL8370N数据手册实战:寄存器、SMI与交换芯片调试要诀

简介:这份资源是Realtek RTL8370NI-VB-CG芯片的官方数据手册,面向网络设备研发工程师、嵌入式软件开发者以及需要了解二层管理型交换机控制器细节的技术人员。手册系统介绍了8端口10/100/1000M自适应交换控制器的功能架构、VLAN/QoS/安全机制、Web/CLI/S…

2026/10/15 4:47:54 阅读更多 →
RTSP协议深度解析:从核心原理到Python实战与避坑指南

RTSP协议深度解析:从核心原理到Python实战与避坑指南

1. 为什么值得花时间吃透RTSP协议如果你做过网络摄像头、视频监控、直播推流或者在线教育相关的项目,大概率绕不开一个词——RTSP。全称Real Time Streaming Protocol,实时流传输协议,工作在应用层,专门用来控制音视频流的传输。它…

2026/10/15 4:47:54 阅读更多 →
MIPS核心指令硬件原理与工程避坑指南

MIPS核心指令硬件原理与工程避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 4:46:53 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →