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

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

文章目录

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

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

1. 代理机制概述

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

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

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


2. JDK 动态代理源码解析

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

核心类和方法

java 复制代码
Object proxy = Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            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:控制哪些方法需要拦截
java 复制代码
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        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 代理
java 复制代码
@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 核心方法

java 复制代码
protected Object createProxy(BeanFactory beanFactory, Object target, String beanName) {
    ProxyFactory proxyFactory = new ProxyFactory();
    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.proceed Advisor 链: MethodBeforeAdvice Advisor 链: AroundAdvice 调用目标方法 Advisor 链: AfterReturning/AfterThrowing 返回客户端


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

java 复制代码
@Aspect
@Component
@Order(1)
public class LoggingAspect {
    @Before("execution(* com.example.service..*.*(..))")
    public void logBefore(JoinPoint jp) {
        System.out.println("日志切面前置通知: " + jp.getSignature());
    }
}

@Aspect
@Component
@Order(2)
public class PerformanceAspect {
    @Around("execution(* com.example.service..*.*(..))")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        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 流程图直观展示代理生成和方法调用链


结束语

👨‍💻 关于我

持续学习 | 追求真我

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

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

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

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

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

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

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

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

关于"掰开揉碎讲编程"系列

编程的世界常常让初学者望而生畏------晦涩的术语、抽象的概念、复杂的原理,像是一座座难以逾越的高山。但学习编程,本不该如此艰难。

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

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

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

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

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

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

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

相关推荐
回家路上绕了弯8 小时前
分布式与集群:90%的开发者都混淆的两个概念
分布式·后端
PieroPC8 小时前
NiceGui 3.4.0 的 ui.pagination 分页实现 例子
前端·后端
十月南城8 小时前
分布式锁与幂等的边界——正确的锁语义、过期与续约、业务层幂等配合
后端
小兔崽子去哪了8 小时前
文件上传专题
java·javascript
香气袭人知骤暖8 小时前
Nacos 服务发现保证机制解析
java·spring·服务发现
精神病不行计算机不上班8 小时前
[Java Web]Java Servlet基础
java·前端·servlet·html·mvc·web·session
不爱学英文的码字机器8 小时前
【征文计划】从一个小模板开始,深入Rokid AR生态
后端·ar·restful
代码扳手8 小时前
从0到1揭秘!Go语言打造高性能API网关的核心设计与实现
后端·go·api
Clarence Liu8 小时前
Golang slice 深度原理与面试指南
开发语言·后端·golang