Spring AOP源码解析

前言

  Spring AOP(面向切面编程)是 Spring 框架的核心模块之一,其底层通过 动态代理 和 字节码增强 实现。以下是 Spring AOP 的源码解析,涵盖核心流程、关键类及实际应用场。

一、Spring AOP 的核心组件

  1. 核心接口
  • Joinpoint:表示程序执行的点(如方法调用、异常抛出),是切面的基础。
  • Advice:切面的具体行为,分为:
    • BeforeAdvice(前置通知)
    • AfterAdvice(后置通知)
    • AroundAdvice(环绕通知)
    • ThrowsAdvice(异常通知)
  • Pointcut:定义哪些 Joinpoint 需要被拦截(通过类和方法匹配规则)。
  • Advisor:组合 Advice 和 Pointcut,表示一个完整的切面。
  1. 代理机制
    Spring AOP 的代理分为两种:
    • JDK 动态代理:基于接口的代理(要求目标类实现接口)。
    • CGLIB 代理:基于子类继承的代理(无需接口,直接生成目标类的子类)。

二、源码解析:动态代理的创建

  1. 代理对象的生成入口
  • Spring AOP 的核心入口是 AopProxy 接口,其实现类为:
    • JdkDynamicAopProxy(JDK 动态代理)
    • CglibAopProxy(CGLIB 代理)
      源码入口:DefaultAopProxyFactory#createAopProxy()
	public AopProxy createAopProxy(AdvisedSupport config) {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {// 选择 CGLIB 代理return new CglibAopProxy(config);} else {// 选择 JDK 动态代理return new JdkDynamicAopProxy(config);}}
  1. JDK 动态代理实现
    关键类:JdkDynamicAopProxy
    • 代理对象生成:通过 Proxy.newProxyInstance() 创建代理对象。
    • 方法拦截:实现 InvocationHandler 接口,重写 invoke() 方法。
      源码示例:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 1. 获取目标方法和拦截器链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 2. 如果无拦截器,直接调用原方法if (chain.isEmpty()) {return method.invoke(target, args);}// 3. 构建 MethodInvocation,执行拦截器链MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);return invocation.proceed();
}
  1. CGLIB 代理实现
    关键类:CglibAopProxy
    • 代理对象生成:通过 Enhancer 类生成目标类的子类。
    • 方法拦截:实现 MethodInterceptor 接口,重写 intercept() 方法。

源码示例:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {// 1. 获取拦截器链List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 2. 构建 CglibMethodInvocation,执行拦截器链CglibMethodInvocation invocation = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);return invocation.proceed();
}

三、拦截器链的执行流程

  1. 拦截器链的触发
      无论是 JDK 动态代理还是 CGLIB 代理,最终都会调用 ReflectiveMethodInvocation#proceed() 方法,触发拦截器链的执行。

  源码入口:ReflectiveMethodInvocation#proceed()

public Object proceed() throws Throwable {// 1. 如果所有拦截器已执行,调用原始方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}// 2. 按顺序执行拦截器链Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof MethodInterceptor) {MethodInterceptor mi = (MethodInterceptor) interceptorOrInterceptionAdvice;return mi.invoke(this);} else {// 动态匹配器处理return ((InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice).interceptor.invoke(this);}
}
  1. Advice 的链式调用
  • MethodInterceptor 的实现类(如 MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor)负责调用具体的 Advice。
  • 执行顺序:
    Around Advice → Before Advice → 目标方法 → Around Advice → After Advice → AfterReturning/AfterThrowing Advice。

四、实际应用中的关键点

  1. 定义切面
    使用 @Aspect 注解定义切面,结合 @Pointcut 和 Advice 注解:
@Aspect
@Component
public class LoggingAspect {@Pointcut("execution(* com.example.service.*.*(..))")public void serviceLayer() {}@Before("serviceLayer()")public void logBefore(JoinPoint joinPoint) {System.out.println("Method called: " + joinPoint.getSignature());}
}
  1. 配置代理模式
    在 Spring Boot 中通过配置选择代理方式:
# 强制使用 CGLIB 代理
spring.aop.proxy-target-class=true
  1. 调试 AOP
  • 查看代理对象:注入的 Bean 如果是代理对象,类名会包含 $EnhancerBySpringCGLIB $ 或 $Proxy。
  • 日志输出:开启 Spring AOP 日志:
logging.level.org.springframework.aop=DEBUG

五、源码设计亮点

  1. 责任链模式

    • 拦截器链(MethodInterceptor)通过责任链模式实现,支持多个 Advice 的链式调用。
  2. 适配器模式

    • 将不同类型的 Advice(如 BeforeAdvice、AfterAdvice)适配为统一的 MethodInterceptor 接口:
      MethodBeforeAdviceAdapter 将 MethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor。
  3. 动态匹配

    • 通过 Pointcut 动态匹配方法,减少不必要的代理逻辑。

六、常见问题与解决方案

  1. 代理失效问题
    • 自调用问题:同类方法内部调用 @Aspect 方法不会触发代理(需通过 AopContext.currentProxy() 获取代理对象)。
    • Bean 未被 Spring 管理:确保切面类被 Spring 扫描(添加 @Component 注解)。
  2. 性能优化
    • 避免过于宽泛的 Pointcut(如 execution(* com.example….(…)))。
    • 优先使用 JDK 动态代理(CGLIB 生成子类可能更耗时)。
  3. 多切面执行顺序
    • 通过 @Order 注解或实现 Ordered 接口控制切面优先级:
@Aspect
@Order(1)
public class SecurityAspect { /* ... */ }

七、总结

  Spring AOP 通过动态代理和拦截器链机制实现切面编程,核心类包括 AopProxy、MethodInterceptor 和 ReflectiveMethodInvocation。理解源码中的责任链模式和适配器模式,有助于解决实际开发中的代理失效、性能优化等问题。对于复杂场景,可结合 AspectJ 实现编译时织入(如 @AspectJ 注解 + 编译插件)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/19224.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu22.04 Deepseek-R1本地容器化部署/内网穿透/OPENWEBUI,打造个人AI助手!

1. 前言 本地部署DeepSeek并实现内网穿透&#xff0c;为家庭成员提供强大的AI支持。通过使用Ollama、Docker、OpenWebUI和Nginx&#xff0c;内网穿透&#xff0c;我们可以轻松实现快速响应和实时搜索功能。 2.软硬件环境 系统&#xff1a;ubuntu22.04, cuda12GPU: RTX2080Ti …

DeepSeek与ChatGPT的全面对比

在人工智能&#xff08;AI&#xff09;领域&#xff0c;生成式预训练模型&#xff08;GPT&#xff09;已成为推动技术革新的核心力量。OpenAI的ChatGPT自发布以来&#xff0c;凭借其卓越的自然语言处理能力&#xff0c;迅速占据市场主导地位。然而&#xff0c;近期中国AI初创公…

[HarmonyOS]鸿蒙(添加服务卡片)推荐商品 修改卡片UI(内容)

什么是服务卡片 &#xff1f; 鸿蒙系统中的服务卡片&#xff08;Service Card&#xff09;就是一种轻量级的应用展示形式&#xff0c;它可以让用户在不打开完整应用的情况下&#xff0c;快速访问应用内的特定功能或信息。以下是服务卡片的几个关键点&#xff1a; 轻量级&#…

【数据结构】 栈和队列

在计算机科学的世界里&#xff0c;数据结构是构建高效算法的基础。栈&#xff08;Stack&#xff09;和队列&#xff08;Queue&#xff09;作为两种基本且重要的数据结构&#xff0c;在软件开发、算法设计等众多领域都有着广泛的应用。今天&#xff0c;我们就来深入探讨一下栈和…

「软件设计模式」桥接模式(Bridge Pattern)

深入解析桥接模式&#xff1a;解耦抽象与实现的艺术 一、模式思想&#xff1a;正交维度的优雅解耦 桥接模式&#xff08;Bridge Pattern&#xff09;通过分离抽象&#xff08;Abstraction&#xff09;与实现&#xff08;Implementation&#xff09;&#xff0c;使二者可以独立…

新建github操作

1.在github.com的主页根据提示新建一个depository。 2.配置用户名和邮箱 git config --global user.name "name" git config --global user.email "email" 3.生成ssh秘钥 ssh-keygen -t rsa 找到public key 对应的文件路径 cat /root/.ssh/id_rsa 复制显…

【力扣】108.将有序数组转换为二叉搜索树

AC截图 题目 思路 因为nums数组是严格递增的&#xff0c;所以只需要每次选出中间节点&#xff0c;然后用左边部分构建左子树&#xff0c;用右边部分构建右子树。 代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* …

如何在 Mac 上解决 Qt Creator 安装后应用程序无法找到的问题

在安装Qt时&#xff0c;遇到了一些问题&#xff0c;尤其是在Mac上安装Qt后&#xff0c;发现Qt Creator没有出现在应用程序中。通过一些搜索和操作&#xff0c;最终解决了问题。以下是详细的记录和解决方法。 1. 安装Qt后未显示Qt Creator 安装完成Qt后&#xff0c;启动应用程…

Spring AI发布!让Java紧跟AI赛道!

1. 序言 在当今技术发展的背景下&#xff0c;人工智能&#xff08;AI&#xff09;已经成为各行各业中不可忽视的重要技术。无论是在互联网公司&#xff0c;还是传统行业&#xff0c;AI技术的应用都在大幅提升效率、降低成本、推动创新。从智能客服到个性化推荐&#xff0c;从语…

数据库脚本MySQL8转MySQL5

由于生产服务器版本上部署的是MySQL5&#xff0c;而开发手里的脚本代码是MySQL8。所以只能降版本了… 升级版本与降级版本脚本转换逻辑一样 MySQL5与MySQL8版本SQL脚本区别 大多数无需调整、主要是字符集与排序规则 MySQL5与MySQL8版本SQL字符集与排序规则 主要操作&…

STM32物联网终端实战:从传感器到云端的低功耗设计

STM32物联网终端实战&#xff1a;从传感器到云端的低功耗设计 一、项目背景与挑战分析 1.1 物联网终端典型需求 &#xff08;示意图说明&#xff1a;传感器数据采集 → 本地处理 → 无线传输 → 云端存储&#xff09; 在工业物联网场景中&#xff0c;终端设备需满足以下核心需…

牛客寒假训练营3

M 牛客传送门 代码如下: const int N2e610,M1e410; const int INF0x3f3f3f3f; const int mod998244353; ll n;void solve(){string s; cin >> s;string ns"nowcoder";sort(s.begin(),s.end(…

BY组态:构建灵活、可扩展的自动化系统

引言 在现代工业自动化领域&#xff0c;BY组态&#xff08;Build Your Own Configuration&#xff09;作为一种灵活、可扩展的解决方案&#xff0c;正逐渐成为工程师和系统集成商的首选。BY组态允许用户根据具体需求自定义系统配置&#xff0c;从而优化生产效率、降低成本并提…

DeepSeek 通过 API 对接第三方客户端 告别“服务器繁忙”

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 上一期分享了如何在本地部署 DeepSeek R1 模型&#xff0c;但通过命令行运行的本地模型&#xff0c;问答的交互也要使用命令行&#xff0c;体验并不是很好。这期分享几个第三方客户端&#xff0c;涵盖了桌…

【第10章:自然语言处理高级应用—10.4 NLP领域的前沿技术与未来趋势】

各位技术探险家们,今天我们要开启一场穿越语言智能奇点的时空之旅。从正在改写物理定律的万亿参数大模型,到能看懂《星际穿越》剧本的跨模态AI,再到正在颠覆编程方式的神经-符号混合系统……这篇万字长文将带你摸清NLP技术进化的七块关键拼图。(建议边读边做笔记,文末有技…

自动驾驶---如何打造一款属于自己的自动驾驶系统

在笔者的专栏《自动驾驶Planning决策规划》中&#xff0c;主要讲解了行车的相关知识&#xff0c;从Routing&#xff0c;到Behavior Planning&#xff0c;再到Motion Planning&#xff0c;以及最后的Control&#xff0c;笔者都做了相关介绍&#xff0c;其中主要包括算法在量产上…

探索 DeepSeek:AI 领域的璀璨新星

在人工智能飞速发展的当下&#xff0c;DeepSeek 作为行业内的重要参与者&#xff0c;正以独特的技术和广泛的应用备受瞩目。 DeepSeek 是一家专注于实现 AGI&#xff08;通用人工智能&#xff09;的中国人工智能公司。它拥有自主研发的深度学习框架&#xff0c;能高效处理海量…

centos部署open-webui

提示&#xff1a;本文将简要介绍一下在linux下open-webui的安装过程,安装中未使用虚拟环境。 文章目录 一、open-webui是什么&#xff1f;二、安装流程1.openssl升级2.Python3.11安装3.sqlite安装升级4.pip 下载安装open-webui 总结 一、open-webui是什么&#xff1f; Open W…

驱动开发、移植(最后的说法有误,以后会修正)

一、任务明确&#xff1a;把创龙MX8的驱动 按照我们的要求 然后移植到 我们的板子 1.Linux系统启动卡制作&#xff0c; sd卡 先按照 《用户手册—3-2-Linux系统启动卡制作及系统固化》 把创龙的Linux系统刷进去。 2. 把TLIMX8-EVM的板子过一遍 把刚刚烧好系统的sd卡插入 创…

SpringBoot+uniApp日历备忘录小程序系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码1.日历渲染代码&#xff1a;2.保存备忘录代码&#xff1a;3.删除备忘录代码&#xff1a; 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBootuniApp框架开…