[Spring AOP 8] Spring AOP 源码全流程总结

Spring AOP总结

更美观清晰的版本在:Github

前面的章节:
[Spring AOP 1] 从零开始的JDK动态代理
[Spring AOP 2] 从零开始的CGLIB动态代理
[Spring AOP 3] Spring选择代理
[Spring AOP 4] Spring AOP 切点匹配
[Spring AOP 5] 高级切面与低级切面:@Aspect vs Advisor
[Spring AOP 6] 静态通知调用
[Spring AOP 7] 动态通知调用

我们以日常在Spring Boot中编写AOP进行方法增强的代码为例来整体梳理AOP背后到底发生了什么。我们需要:

  1. 配置类:开启代理
  2. 切面类:这里准备了一个前置通知和一个环绕通知
  3. 目标类
  4. 启动类
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 将会使用CGLIB代理
@ComponentScan(basePackages = "spring.aop.summarization")
public class AopConfig {}
@Slf4j
@Aspect
@Component
public class MyAspect {// 前置增强foo方法@Before("execution(* spring.aop.summarization.Target.foo(..))")public void beforeAdvice(JoinPoint joinPoint) {log.info("Foo is ready to do something...");}// 环绕增强bar方法@Around("execution(* spring.aop.summarization.Target.bar(..))")public Object executionTime(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Bar is ready to do something...");long start = System.currentTimeMillis();Object result = joinPoint.proceed();long end = System.currentTimeMillis();log.info("Bar execution time: {}", end - start);return result;}
}
@Component
@Slf4j
public class Target {public void foo() {fooDoSomething();}public void bar() {barDoSomething();}public void fooDoSomething() {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}public void barDoSomething() {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
@SpringBootApplication
public class MyApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);Target target = (Target) context.getBean("target"); // 获得代理对象target.foo(); // 代理对象将调用增强了的方法target.bar();}
}

我们运行启动类,可以看到预期的输出结果:

  • foo方法被前置增强
  • bar方法被环绕增强
2025-05-08T11:16:12.382+01:00  INFO 13260 --- [           main] spring.aop.summarization.MyAspect        : Foo is ready to do something...
2025-05-08T11:16:13.384+01:00  INFO 13260 --- [           main] spring.aop.summarization.MyAspect        : Bar is ready to do something...
2025-05-08T11:16:14.386+01:00  INFO 13260 --- [           main] spring.aop.summarization.MyAspect        : Bar execution time: 1002

接下来,我们主要关注Spring AOP中发生的事情。

  1. 解析切面定义
    Spring 启动时扫描所有 @Aspect 注解的类(如 MyAspect),并把带有 @Before@Around@After 等方法标记的元信息提取出来,交给一个 ReflectiveAspectJAdvisorFactory 去生成“候选” Advisor

  2. 组装低级 Advisor
    ReflectiveAspectJAdvisorFactory 会将每个切面方法分装为一个 Pointcut + Advice

    1. 对于 @Before("…"),生成一个 AspectJExpressionPointcut 和一个 AspectJMethodBeforeAdvice,再封装成一个 DefaultPointcutAdvisor
    2. 对于 @Around("…"),生成一个 AspectJExpressionPointcut 和一个 AspectJAroundAdvice,再封装成另一个 DefaultPointcutAdvisor
    3. 所有这些 Advisor 都被当成候选切面缓存下来,等待后续匹配。
    4. 同时,Spring 也注册了一个关键的 BeanPostProcessorAnnotationAwareAspectJAutoProxyCreator
      1. AnnotationAwareAspectJAutoProxyCreator后续会根据切面类型自动生成代理对象
  3. 容器刷新 & BeanPostProcessor 注册
    SpringApplication.run(...) 完成扫描和注册后,容器会把 AnnotationAwareAspectJAutoProxyCreator 等所有 BeanPostProcessor 都注册到生命周期中。

  4. Bean 实例化与初始化(省略细节)
    对每一个普通 Bean(比如:Target),

    1. Spring会用构造器创建实例并进行依赖注入;
      • 如果Bean没有循环依赖,那么AnnotationAwareAspectJAutoProxyCreator创建代理对象的时机在Bean初始化后
      • 如果Bean被检测到有循环依赖,那么AnnotationAwareAspectJAutoProxyCreator创建代理对象的时机在依赖注入前
    2. 进行Bean的后处理;
    3. 进入AOP代理逻辑;
  5. 自动代理 (wrapIfNecessary)
    在Bean的后处理中:

    1. AnnotationAwareAspectJAutoProxyCreator#wrapIfNecessary判断
      • 这个类是不是基础设施(切面本身、Advisor、Advice 等)如果是,跳过;否则执行findEligibleAdvisors()
    2. 调用 findEligibleAdvisors():它会拿之前缓存的所有候选 Advisor,对每一个执行静态匹配,找到所有匹配当前 Bean 的切面;
    3. 若结果不为空,就新建一个 ProxyFactory,决定用 JDK 代理还是 CGLIB,给它设置目标对象和接口/类信息;
      • proxyTargetClass = false && 目标实现接口:JDK
      • proxyTargetClass = false && 目标未实现接口:CGLIB
      • proxyTargetClass = true:CGLIB
    4. 把上一步过滤出的 Advisor 全部加到 ProxyFactory
    5. 调用 proxyFactory.getProxy(),生成代理对象,并替换掉容器中原来的 Bean 引用。
  6. 代理对象注入
    以后别的 Bean 依赖 Target 时,Spring 注入的都是上面生成的代理(JdkDynamicAopProxyObjenesisCglibDynamicAopProxy),而不是直接的 new Target()

  7. 运行时方法调用 & 拦截链执行
    当调用 target.foo()target.bar()时:

    1. 代理对象拦截调用,进入 JdkDynamicAopProxy#invokeCglibAopProxy#intercept
      1. 先执行AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice()
        • 将所有通知统一转换为静态通知
        • 或者拿到动态通知,但我们这个例子中没有动态通知
      2. 再先根据 Advisor 列表为该方法构建 MethodInterceptor
        • ExposeInvocationInterceptor会创建一个最外围的ADVISOR切面
        • 同时会将调用链存入自己的ThreadLocal
    2. 调用 ReflectiveMethodInvocation.proceed(),沿链执行(递归):
      • Before Advice@Before)── 执行 beforeAdvice()
      • Around Advice@Around)── 调用 proceed() 前逻辑;
      • 目标方法Target#foo / bar)实际上被调用;
      • 开始一层层退出,Around Advice后逻辑被执行
      • 退出Before Advice
    3. 链条执行完毕,最终结果返回给调用者。

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

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

相关文章

C#高级编程:加密解密

在数字化时代,数据安全是每个应用程序都必须重视的环节。无论是用户的个人信息、敏感的商业数据,还是重要的系统配置,都需要得到妥善的保护。C# 作为一种广泛应用的编程语言,提供了丰富且强大的加密解密功能,帮助开发者构建安全可靠的应用。本文将深入探讨 C# 高级编程中的…

基于运动补偿的前景检测算法

这段代码实现了基于运动补偿的前景检测算法。 主要功能包括: 运动补偿模块:使用基于网格的 KLT 特征跟踪算法计算两帧之间的运动,然后通过单应性变换实现帧间运动补偿。前景检测模块:结合两帧运动补偿结果,通过帧间差…

使用matlab进行数据拟合

目录 一、工作区建立数据 二、曲线拟合器(在"APP"中) 三、曲线拟合函数及参数 四、 在matlab中编写代码 一、工作区建立数据 首先,将数据在matlab工作区中生成。如图1所示: 图 1 二、曲线拟合器(在"APP"中) 然后,…

Playwright 安装配置文件详解

Playwright 安装&配置文件详解 环境准备 Node.js 14.0(推荐 LTS 版本)npm(推荐使用最新版)支持 Windows、macOS、Linux 一步到位的官方推荐安装方式 1. 进入你的项目目录 # Windows cd 路径\到\你的项目 # macOS/Linux cd…

中国古代史4

东汉 公元25年,刘秀建立东汉,定都洛阳,史称光武中兴 白马寺:汉明帝时期建立,是佛教传入中国后兴建的第一座官办寺院,有中国佛教的“祖庭”和“释源”之称,距今1900多年历史 班超—西域都护—投…

springboot + mysql8降低版本到 mysql5.7

springboot mysql8降低版本到 mysql5.7 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency>spring:datasource:driverClassName: com.mysql.jdbc.D…

4.4java常用类

在 Java 中&#xff0c;System 和 Runtime 类都是 java.lang 包下非常重要的类&#xff0c;它们提供了与系统交互以及管理 Java 虚拟机&#xff08;JVM&#xff09;运行时环境的功能。 System 类 System 类包含了一些有用的类字段和方法&#xff0c;它不能被实例化&#xff0…

【嵌入式笔记】Modbus TCP

1.概述 定义&#xff1a;Modbus TCP 是 Modbus 协议的变体&#xff0c;基于 TCP/IP 协议栈&#xff0c;用于通过以太网实现工业设备间的通信。 背景&#xff1a;由施耐德电气&#xff08;原 Modicon 公司&#xff09;在 1999 年发布&#xff0c;将传统的 Modbus RTU/ASCII 适配…

《解锁React Native与Flutter:社交应用启动速度优化秘籍》

React Native和Flutter作为当下热门的跨平台开发框架&#xff0c;在优化应用启动性能方面各有千秋。今天&#xff0c;我们就深入剖析它们独特的策略与方法。 React Native应用的初始包大小对启动速度影响显著。在打包阶段&#xff0c;通过精准分析依赖&#xff0c;去除未使用的…

R语言学习--Day02--实战经验反馈

最近在做需要用R语言做数据清洗的项目&#xff0c;在网上看再多的技巧与语法&#xff0c;都不如在项目中实战学习的快&#xff0c;下面是我通过实战得来的经验。 判断Rstudio是否卡死 很多时候&#xff0c;我们在运行R语言代码时&#xff0c;即使只是运行框选的几行代码&#…

How Sam‘s Club nudge customers into buying more

Here’s how Sam’s Club (or similar warehouse memberships) nudge customers into buying more: It’s a classic psychological strategy rooted in sunk cost fallacy and loss aversion. 1. Prepaid Membership Creates a “Sunk Cost” Once you’ve paid the annual …

OpenHarmony系统HDF驱动开发介绍(补充)

一、HDF驱动简介 HDF&#xff08;Hardware Driver Foundation&#xff09;驱动框架&#xff0c;为驱动开发者提供驱动框架能力&#xff0c;包括驱动加载、驱动服务管理、驱动消息机制和配置管理。 简单来说&#xff1a;HDF框架的驱动和Linux的驱动比较相似都是由配置文件和驱动…

自然语言处理 (NLP) 入门:NLTK 与 SpaCy 的初体验

自然语言处理入门&#xff1a;NLTK 与 SpaCy 的初体验 在当今数字化飞速发展的浪潮中&#xff0c;自然语言处理&#xff08;NLP&#xff09;已经成为了极具热度的技术领域。自然语言处理的核心目标是让计算机能够理解、分析并生成人类语言&#xff0c;其应用场景极为广泛&…

LLaVA:开源多模态大语言模型深度解析

一、基本介绍 1.1 项目背景与定位 LLaVA(Large Language and Vision Assistant)是由Haotian Liu等人开发的开源多模态大语言模型,旨在实现GPT-4级别的视觉-语言交互能力。该项目通过视觉指令微调技术,将预训练的视觉编码器与语言模型深度融合,在多个多模态基准测试中达到…

如何利用大模型对文章进行分段,提高向量搜索的准确性?

利用大模型对文章进行分段以提高向量搜索准确性,需结合文本语义理解、分块策略优化以及向量表示技术。以下是系统性的解决方案: 一、分块策略的核心原则 语义完整性优先 分块需确保每个文本单元在语义上独立且完整。研究表明,当分块内容保持单一主题时,向量嵌入的语义表征能…

Java高频面试之并发编程-17

volatile 和 synchronized 的区别 在 Java 并发编程中&#xff0c;volatile 和 synchronized 是两种常用的同步机制&#xff0c;但它们的适用场景和底层原理有显著差异。以下是两者的详细对比&#xff1a; 1. 核心功能对比 特性volatilesynchronized原子性不保证复合操作的原…

技术债务积累,如何进行有效管理

识别和评估技术债务、明确技术债务的优先级、制定系统的还债计划、持续监控与预防技术债务产生是有效管理技术债务积累的重要策略。其中尤其要注重识别和评估技术债务&#xff0c;只有准确识别技术债务的种类和严重程度&#xff0c;才能制定出高效且有针对性的解决方案&#xf…

安装windows版本的nacos

一、下载nacos安装包 浏览器搜索nacos&#xff0c;进入nacos官网 https://nacos.io/docs/latest/overview/ 选择下载windows版本的nacos 二、解压缩 三、进入bin目录&#xff0c;cmd命令行窗口 四、启动nacos 查看日志 五、打开可视化页面查看 以上&#xff0c;就是安装wind…

小结:Android系统架构

https://developer.android.com/topic/architecture?hlzh-cn Android系统的架构&#xff0c;分为四个主要层次&#xff1a;应用程序层、应用框架层、库和运行时层以及Linux内核层。&#xff1a; 1. 应用程序层&#xff08;Applications&#xff09; 功能&#xff1a;这一层包…

鸿蒙5.0项目开发——鸿蒙天气项目的实现(欢迎页)

【高心星出品】 文章目录 欢迎页面效果数据字典创建数据库表格Splash页面页面功能欢迎页代码亮点 项目按照从数据库连接层–视图层–业务逻辑层这种三层架构开发&#xff0c;所以先设计了数据库表格的结构&#xff0c;在EntryAbility中创建表格。 欢迎页面效果 数据字典 sear…