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

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

文章目录


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 实现。

核心类和方法

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

流程示意

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

特点

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

3. CGLIB 代理源码解析

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

核心类

  • Enhancer:创建代理类
  • MethodInterceptor:拦截方法调用
  • CallbackFilter:控制哪些方法需要拦截
Enhancer enhancer =newEnhancer(); enhancer.setSuperclass(TargetClass.class); enhancer.setCallback(newMethodInterceptor(){@OverridepublicObjectintercept(Object obj,Method method,Object[] args,MethodProxy proxy)throwsThrowable{System.out.println("方法调用前");Object result = proxy.invokeSuper(obj, args);System.out.println("方法调用后");return result;}});TargetClass proxy =(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(BeanFactory beanFactory,Object target,String beanName){ProxyFactory proxyFactory =newProxyFactory(); proxyFactory.setTarget(target); proxyFactory.addAdvisors(this.findAdvisors(beanFactory, beanName));return proxyFactory.getProxy(getProxyClassLoader());}

6. 方法调用链源码解析

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

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

客户端调用代理方法ReflectiveMethodInvocation.proceedAdvisor 链: MethodBeforeAdviceAdvisor 链: AroundAdvice调用目标方法Advisor 链: AfterReturning/AfterThrowing返回客户端


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

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

多切面调用顺序示意

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 流程图直观展示代理生成和方法调用链


结束语

👨‍💻 关于我

持续学习 | 追求真我

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

感谢订阅专栏 三连文章

image-20251011155556997

掘金点击访问QiunerZEEKLOG点击访问QiunerGitHub点击访问QiunerGitee点击访问Qiuner

专栏简介
📊 一图读懂系列图文并茂,轻松理解复杂概念
📝 一文读懂系列深入浅出,全面解析技术要点
🌟持续更新保持学习,不断进步
🎯 人生经验经验分享,共同成长
你好,我是Qiuner. 为帮助别人少走弯路而写博客

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

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

如果你遇到了问题,自己没法解决,可以去我掘金评论区问。ZEEKLOG评论区和私信消息看不完 掘金消息少一点.
上一篇推荐链接
Java程序员快又扎实的学习路线点击该处自动跳转查看哦
一文读懂 AI点击该处自动跳转查看哦
一文读懂 服务器点击该处自动跳转查看哦
2024年创作回顾点击该处自动跳转查看哦
一文读懂 ESLint配置点击该处自动跳转查看哦
老鸟如何追求快捷操作电脑点击该处自动跳转查看哦
未来会写什么文章?预告链接
一文读懂 XX?点击该处自动跳转查看哦
2025年终总结点击该处自动跳转查看哦
一图读懂 XX?点击该处自动跳转查看哦

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

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

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

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

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

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

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

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

Read more

【Linux网络系列】:JSON+HTTP,用C++手搓一个web计算器服务器!

【Linux网络系列】:JSON+HTTP,用C++手搓一个web计算器服务器!

🔥 本文专栏:Linux网络Linux实践系列 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录:别害怕选错,人生最遗憾的从不是‘选错了’,而是‘我本可以’。每一次推倒重来的勇气,都是在给灵魂贴上更坚韧的勋章。 ★★★ 本文前置知识: 序列化与反序列化 引入 在之前的博客中,我详细介绍了序列化 与反序列化 的概念。对于使用 TCP 协议进行通信的双方,由于 TCP 是面向字节流的,在发送数据之前,我们通常需要定义一种结构化的数据来描述传输内容,并以此作为数据的容器。在 C++ 中,这种结构化数据通常表现为对象或结构体。然而,我们不能直接将结构体内存中对应的字节原样发送到另一端,因为直接传递内存字节会引发字节序 和结构体内存对齐 的问题。不同平台、不同编译器所遵循的内存对齐规则可能不同,这可能导致接收方在解析结构体字段时出现错误。 因此,我们需要借助序列化 。序列化 是指将结构化的数据按照预定的规则转换为连续的字节流。其主要目的是屏蔽平台差异,使得位于不同平台的进程能够以统一的方式解析该字节流。序列化通常分为两种形式:文本序列化 与二进制序列化 。 文

By Ne0inhk
C++迭代器全解析:从概念到实践,掌握STL的灵魂

C++迭代器全解析:从概念到实践,掌握STL的灵魂

引言:为什么需要迭代器? 在C++的世界里,数据容器千变万化——有连续存储的vector,有链式连接的list,还有树形结构的set。如果每种容器都要单独设计访问接口,那么算法的复用性将大大降低。这正是迭代器(Iterator)诞生的意义:提供一种统一的访问机制,让算法可以独立于具体容器而工作。 想象一下,如果没有迭代器,我们需要为每个容器单独实现sort()、find()、copy()等算法。而有了迭代器,一个std::sort()就能处理所有支持随机访问的容器。这就是STL(标准模板库)设计哲学的核心——泛型编程。 迭代器的本质:泛型指针 从概念上讲,迭代器是泛化的指针。普通指针能做的,迭代器基本都能做,而且更安全、更抽象。但并非所有迭代器都像指针那样强大,这正是STL将迭代器分为五种类别的原因。 // 原生指针本身也是迭代器 int arr[5] = {1, 2, 3, 4, 5}; int* ptr

By Ne0inhk
C++分布式语音识别服务实践

C++分布式语音识别服务实践

基于 brpc+etcd + 百度 AI SDK 的分布式语音识别服务实践:从代码架构到踩坑复盘 一、项目背景与核心功能 最近基于 C++ 实现了一个分布式语音识别子服务,核心目标是提供高可用的 RPC 接口,支持客户端上传 PCM 音频文件并返回识别结果。技术栈选型如下: * RPC 框架:brpc(百度开源高性能 RPC 框架,支持多种协议); * 数据序列化:Protobuf(定义 RPC 接口和数据结构); * 服务注册与发现:etcd(分布式键值存储,实现服务上下线感知); * 语音识别能力:百度 AI 语音 SDK(提供成熟的 PCM 音频转文字能力); * 日志与配置:spdlog(高性能日志库)、gflags(命令行参数解析)。 项目分为服务端和客户端两部分:

By Ne0inhk
Java开发新变革!飞算JavaAI深度剖析与实战指南

Java开发新变革!飞算JavaAI深度剖析与实战指南

摘要:文章通过分析Java开发领域的现状和挑战,引出了飞算JavaAI这一创新工具。它能显著提升开发效率,降低重复编码工作,并保障代码质量。文章详细介绍了飞算JavaAI的核心功能,包括自然语言输入、全流程自动化和完整工程源码输出等,并通过电商系统和企业级项目等实战案例展示了其强大性能。与其他工具的对比进一步凸显了飞算JavaAI的优势,使其成为Java开发者提升效率和创新能力的强大助力。 目录 引言:Java 开发的新时代曙光 一、飞算 JavaAI:Java 开发的变革者 (一)定义与定位 (二)核心功能深度解读 (三)独特优势彰显实力 二、飞算 JavaAI 注册使用流程详解 (一)注册流程图文并茂 (二)首次使用引导 (三)常见问题与解决 编辑 三、实战案例:飞算 JavaAI 大展身手 (一)电商系统开发实战 (二)企业级项目应用案例 (三)经典代码案例 四、

By Ne0inhk