news 2026/6/21 18:59:57

Spring Boot AOP(二) 代理机制解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot AOP(二) 代理机制解析
博主社群介绍: ① 群内初中生、高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。 ② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。 ③ 群内也有职场精英,大厂大佬,跨国企业主管,可交流技术、面试、找工作的经验。 进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬,进群赠送CSDN评论防封脚本,送真活跃粉丝,助你提升文章热度。 群公告里还有全网大赛约稿汇总/博客提效工具集/CSDN自动化运营脚本 有兴趣的加文末联系方式,备注自己的CSDN昵称,拉你进群,互相学习共同进步。

文章目录

  • Spring Boot AOP(二) 代理机制解析
    • 1. 代理机制概述
    • 2. JDK 动态代理源码解析
      • 核心类和方法
      • 流程示意
      • 特点
    • 3. CGLIB 代理源码解析
      • 核心类
      • 调用流程
      • 特点
    • 4. Spring AOP 代理选择机制
      • Mermaid 流程:代理选择逻辑
    • 5. Spring 代理生成核心源码解析
      • 5.1 入口类
      • 5.2 ProxyFactory 核心方法
    • 6. 方法调用链源码解析
    • 7. 实战示例:多切面组合
      • 多切面调用顺序示意
    • 8. 总结
    • 结束语


Spring Boot AOP(二) 代理机制解析

1. 代理机制概述

Spring AOP 的核心在于代理对象,它负责在方法调用前后织入切面逻辑。Spring AOP 默认只对Spring 管理的 Bean生效,并且使用运行时动态代理(JDK 动态代理或 CGLIB 代理)。

代理类型特点使用场景限制
JDK 动态代理基于接口生成代理对象Bean 实现接口只能代理接口方法
CGLIB 代理基于子类生成代理对象Bean 无接口或 proxyTargetClass=truefinal 类或 final 方法无法代理
ByteBuddy(Spring 5 可选)生成字节码动态代理高级场景复杂配置,可替代 CGLIB

Spring Boot 默认自动选择 JDK 或 CGLIB,除非手动配置proxyTargetClass=true强制使用 CGLIB。


2. JDK 动态代理源码解析

JDK 动态代理基于java.lang.reflect.ProxyInvocationHandler实现。

核心类和方法

Objectproxy=Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("方法调用前");Objectresult=method.invoke(target,args);System.out.println("方法调用后");returnresult;}});

流程示意

客户端调用代理对象方法
InvocationHandler.invoke
切面前置逻辑
调用目标对象方法
切面返回/异常逻辑
返回调用方

特点

  • 代理对象和目标对象实现相同接口
  • 方法调用通过InvocationHandler转发
  • 执行链由多个Advice组合而成

3. CGLIB 代理源码解析

CGLIB(Code Generation Library)通过生成目标类的子类,在方法调用中织入切面逻辑。

核心类

  • Enhancer:创建代理类
  • MethodInterceptor:拦截方法调用
  • CallbackFilter:控制哪些方法需要拦截
Enhancerenhancer=newEnhancer();enhancer.setSuperclass(TargetClass.class);enhancer.setCallback(newMethodInterceptor(){@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println("方法调用前");Objectresult=proxy.invokeSuper(obj,args);System.out.println("方法调用后");returnresult;}});TargetClassproxy=(TargetClass)enhancer.create();

调用流程

flowchart TD A[客户端调用代理对象方法] --> B[MethodInterceptor.intercept] B --> C{是否有前置通知?} C -->|是| D[@Before 前置逻辑] C -->|否| E[调用目标对象方法] D --> E E --> F{方法是否抛异常?} F -->|否| G[@AfterReturning 返回通知] F -->|是| H[@AfterThrowing 异常通知] G --> I[@After 后置通知] H --> I I --> J[返回调用方] style A fill:#BBDEFB style B fill:#FFF9C4 style E fill:#FFCDD2 style G fill:#C8E6C9 style H fill:#FFE0B2 style J fill:#E1BEE7

特点

  • 生成目标类子类,支持无接口类
  • 不能代理 final 类或 final 方法
  • 方法调用速度略快于 JDK 动态代理

4. Spring AOP 代理选择机制

Spring 自动选择代理类型:

条件结果
Bean 实现接口且 proxyTargetClass=false使用 JDK 动态代理
Bean 无接口或 proxyTargetClass=true使用 CGLIB 代理
@EnableAspectJAutoProxy(proxyTargetClass=true)// 强制使用 CGLIB

Mermaid 流程:代理选择逻辑

创建 Bean 代理
Bean 实现接口?
proxyTargetClass=true?
使用 CGLIB 代理
使用 JDK 代理

5. Spring 代理生成核心源码解析

5.1 入口类

AnnotationAwareAspectJAutoProxyCreator(AOP 自动代理器)负责:

  1. 扫描 Bean,判断是否匹配切面
  2. 生成 Advisor(切入点 + 通知)
  3. 根据条件选择代理类型
  4. 使用ProxyFactoryEnhancer创建代理对象
BeanPostProcessor.postProcessAfterInitialization
是否匹配切面?
创建 ProxyFactory
选择代理类型 JDK/CGLIB
生成代理对象
替换原 Bean 注入容器
直接返回 Bean

5.2 ProxyFactory 核心方法

protectedObjectcreateProxy(BeanFactorybeanFactory,Objecttarget,StringbeanName){ProxyFactoryproxyFactory=newProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAdvisors(this.findAdvisors(beanFactory,beanName));returnproxyFactory.getProxy(getProxyClassLoader());}

6. 方法调用链源码解析

Spring AOP 方法调用链(以 JDK/CGLIB 统一):

  1. 客户端调用代理对象方法
  2. ReflectiveMethodInvocation封装调用信息
  3. 执行 Advisor 链:
    • MethodBeforeAdvice → AroundAdvice → AfterReturning/AfterThrowing
  4. 最终调用目标方法
  5. 返回结果或异常传递给代理
客户端调用代理方法
ReflectiveMethodInvocation.proceed
Advisor 链: MethodBeforeAdvice
Advisor 链: AroundAdvice
调用目标方法
Advisor 链: AfterReturning/AfterThrowing
返回客户端

7. 实战示例:多切面组合

@Aspect@Component@Order(1)publicclassLoggingAspect{@Before("execution(* com.example.service..*.*(..))")publicvoidlogBefore(JoinPointjp){System.out.println("日志切面前置通知: "+jp.getSignature());}}@Aspect@Component@Order(2)publicclassPerformanceAspect{@Around("execution(* com.example.service..*.*(..))")publicObjectmeasureTime(ProceedingJoinPointpjp)throwsThrowable{longstart=System.currentTimeMillis();Objectresult=pjp.proceed();System.out.println("性能切面耗时: "+(System.currentTimeMillis()-start)+"ms");returnresult;}}

多切面调用顺序示意

flowchart TD A[方法调用] --> B[LoggingAspect @Before] B --> C[PerformanceAspect @Around 前] C --> D[目标方法执行] D --> E[PerformanceAspect @Around 后] E --> F[返回调用方]

8. 总结

  • Spring AOP 核心是代理对象
  • JDK 动态代理针对接口,CGLIB 针对类
  • Spring 自动选择代理类型,可配置proxyTargetClass
  • AnnotationAwareAspectJAutoProxyCreator+ProxyFactory是生成代理的核心
  • 方法调用链由Advisor链统一管理,实现通知执行顺序
  • Mermaid 流程图直观展示代理生成和方法调用链


    结束语


    👨‍💻 关于我

    持续学习 | 追求真我

    如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的。想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎。

    感谢订阅专栏 三连文章

    掘金点击访问Qiuner CSDN点击访问Qiuner GitHub点击访问Qiuner Gitee点击访问Qiuner

    专栏简介
    📊 一图读懂系列图文并茂,轻松理解复杂概念
    📝 一文读懂系列深入浅出,全面解析技术要点
    🌟持续更新保持学习,不断进步
    🎯 人生经验经验分享,共同成长

    你好,我是Qiuner.为帮助别人少走弯路而写博客

    如果本篇文章帮到了你 不妨点个吧~ 我会很高兴的 😄 (^ ~ ^) 。想看更多 那就点个关注吧 我会尽力带来有趣的内容 😎。

    代码都在Github或Gitee上,如有需要可以去上面自行下载。记得给我点星星哦😍

    如果你遇到了问题,自己没法解决,可以去我掘金评论区问。CSDN评论区和私信消息看不完 掘金消息少一点.

    上一篇推荐链接
    Java程序员快又扎实的学习路线点击该处自动跳转查看哦
    一文读懂 AI点击该处自动跳转查看哦
    一文读懂 服务器点击该处自动跳转查看哦
    2024年创作回顾点击该处自动跳转查看哦
    一文读懂 ESLint配置点击该处自动跳转查看哦
    老鸟如何追求快捷操作电脑点击该处自动跳转查看哦
    未来会写什么文章?预告链接
    一文读懂 XX?点击该处自动跳转查看哦
    2025年终总结点击该处自动跳转查看哦
    一图读懂 XX?点击该处自动跳转查看哦

    关于"掰开揉碎讲编程"系列
    编程的世界常常让初学者望而生畏——晦涩的术语、抽象的概念、复杂的原理,像是一座座难以逾越的高山。但学习编程,本不该如此艰难。

    "掰开揉碎讲编程"系列的初衷,就是把那些看似高深的技术知识,像掰开面包一样拆解开来,像揉碎面团一样细细讲透。这里不玩虚的,不堆砌术语,只用最朴实的语言、最贴近生活的比喻,再搭配手绘般的图解示意。抽象的概念画出来,复杂的流程拆开看,让编程知识变得像看图说话一样简单。

    与其他基础教程不同的是,我不会上来就告诉你"怎么装、怎么用"。每一个工具、每一项技术,我都会带你了解它的前世今生——它诞生的背景、要解决的痛点、在整个开发流程中的位置。只有理解了"为什么需要它",才能真正掌握"如何用好它"。

    内容上,这个系列会有两种文章:

    一种是长篇深度文,慢工出细活,把一个技术从头到尾讲清楚——它怎么来的、为什么重要、怎么用、怎么用好。适合系统学习,打牢基础。

    另一种是短篇问题文,专治各种疑难杂症——IDEA汉化后乱码了、Git冲突不知道怎么解、环境变量配置出了岔子等等。遇到问题时翻一翻,快速解决,继续开发。

    这里没有"懂的都懂"式的敷衍,没有"显而易见"的跳跃,每一个概念都会从零开始构建,每一处难点都会反复推敲。就像老师傅手把手教徒弟,我想做的,是让每一个想学编程的人,都能真正理解技术背后的本质。

    无论你是刚接触编程的萌新,还是想要夯实基础的开发者,这个系列都希望成为你的良师益友。让我们一起,把编程这件事,掰开了、揉碎了,彻彻底底搞明白。

    版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
    网站建设 2026/6/19 1:39:19

    前端环境搭建,零基础入门到精通,收藏这篇就够了

    前端环境搭建 一、node.js安装 1、windows上安装node.js(npm) cnpm(淘宝镜像)安装 2、Mac上安装node.js 先安装nvm再用nvm安装node.js 二、Mac下brew(Homebrew)安装 brew安装问题解决 三、vue脚手架(vue-cli&#xf…

    作者头像 李华
    网站建设 2026/6/21 14:14:03

    hive中的视图 view

    好的,我们来详细讲解一下 Hive 中的视图。 什么是视图? 在 Hive 中,视图是一个虚拟表。它本身并不存储数据,而是基于一个或多个底层表(称为基表)或其他视图的查询结果。视图可以被看作是一个命名查询。当你查询视图时,Hive 会执行定义视图时使用的查询语句,并返回结果…

    作者头像 李华
    网站建设 2026/6/14 3:09:58

    flink的二阶段提交流程分析

    好的,我们来详细分析 Flink 的二阶段提交流程(Two-Phase Commit, 2PC)。这个流程是 Flink 实现端到端 Exactly-Once 语义的关键机制之一,尤其在涉及外部系统(如 Kafka、数据库)作为 Sink 时。 核心目标: 确保所有参与计算的算子在发生故障恢复后,能够基于一致的、已提交…

    作者头像 李华
    网站建设 2026/6/18 1:53:33

    yarn向hive提交队列的方式

    在YARN集群中&#xff0c;向Hive任务指定提交队列的方式主要通过以下步骤实现&#xff1a;一、永久性配置&#xff08;修改Hive配置文件&#xff09;修改 hive-site.xml 文件&#xff0c;添加队列参数&#xff1a;<property><name>mapreduce.job.queuename</nam…

    作者头像 李华
    网站建设 2026/6/10 13:52:26

    vue基于springboot的教师讲座听课评课管理系统

    目录已开发项目效果实现截图开发技术介绍系统开发工具&#xff1a;核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&…

    作者头像 李华
    网站建设 2026/6/18 10:01:46

    vue基于springboot的连锁超市门店销售管理系统可视化大屏数据分析系统

    目录 已开发项目效果实现截图开发技术介绍系统开发工具&#xff1a; 核心代码参考示例1.建立用户稀疏矩阵&#xff0c;用于用户相似度计算【相似度矩阵】2.计算目标用户与其他用户的相似度系统测试总结源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式…

    作者头像 李华