spring源码01-spring容器启动流程概述

文章目录

  • 【README】
  • 【1】基于java配置的spring容器
  • 【2】spring容器初始化代码
    • 【2.1】实例化容器AnnotationConfigApplicationContext
    • 【2.2】注册配置类BeanDefinition
    • 【2.3】刷新容器(主要是实例化bean)【重要】
      • 【2.3.1】刷新容器步骤概述

【README】

1)本文使用了spring-6.1.10制品库;

2)本文使用AnnotationConfigApplicationContext容器类型(注解配置应用容器), 基于java配置构建spring应用,无需xml配置;

3)基于spring构建应用系统分为2个阶段(或者狭义理解为spring容器启动过程分为2个阶段)

  1. 容器初始化阶段:
    1. 实例化容器,BeanDefinition读取器,类路径扫描器;
    2. 准备环境,包括注册属性bean(如os环境变量,jvm系统变量)到容器;
    3. 注册BeanFactory后置处理器并触发其后置处理方法,包括触发BeanDefinition注册后置处理器(使用扫描器与读取器读取并注册BeanDefinition到容器),触发BeanFactory后置处理器(如增强配置类);
    4. 注册Bean后置处理器到容器(用户自定义的Bean后置处理器);
    5. 注册消息源MessageSource到容器,用于国际化;
    6. 注册应用事件多播器到容器,用于发布spring各阶段事件;
    7. 注册应用监听器到容器,用于监听spring事件;
  2. bean实例化阶段:大致思想是根据BeanDefinition实例化bean

4)补充:

  • 实例化:就是创建或new的意思;
  • 初始化:可以简单理解为设置属性,装配依赖bean;

【1】基于java配置的spring容器

【JavaBasedContainerUsingSeveralJavaConfigMain】

public class JavaBasedContainerUsingSeveralJavaConfigMain {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 实例化容器 System.out.println("before register()"); context.register(AppConfig00.class, AppConfig02.class); // 注册配置类BeanDefinition System.out.println("after register()");context.refresh(); // 刷新容器(最主要的步骤是实例化bean) System.out.println("after refresh()");context.getBean(HelloService.class).sayHello("Musk");context.getBean(HelloService02.class).sayHello("Trump");}
}

【运行日志】

before register()
after register()
AppConfig00 构造器
AppConfig02 构造器
after refresh()
HelloService#sayHell(): hello Musk
HelloService2#sayHell():  hello Trump

【配置类】

@Configuration
public class AppConfig00 {public AppConfig00() {System.out.println("AppConfig00 构造器");}@Beanpublic HelloService helloService() {return new HelloService();}
}

【服务类】

public class HelloService {public void sayHello(String user) {System.out.println("HelloService#sayHell(): hello " + user);}
}

【pom.xml】 参见 pom.xml



【2】spring容器初始化代码

【2.1】实例化容器AnnotationConfigApplicationContext

1)实例化容器:AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 实例化容器

【AnnotationConfigApplicationContext 】 实例化容器, BeanDefinition读取器,类路径BeanDefinition扫描器

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {// ... 
}
public AnnotationConfigApplicationContext() {StartupStep createAnnotatedBeanDefReader = getApplicationStartup().start("spring.context.annotated-bean-reader.create");// 实例化 BeanDefinition读取器this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end();// 实例化 类路径BeanDefinition扫描器 this.scanner = new ClassPathBeanDefinitionScanner(this); }

【GenericApplicationContext】 实例化BeanFactory,类型为DefaultListableBeanFactory;

public GenericApplicationContext() {this.beanFactory = new DefaultListableBeanFactory();}


【2.2】注册配置类BeanDefinition

1)调用context.register(AppConfig00.class, AppConfig02.class); 注册配置类BeanDefinition ;

AnnotationConfigApplicationContext#register()方法接着调用this.reader.register(componentClasses);

【AnnotationConfigApplicationContext】

public void register(Class<?>... componentClasses) {Assert.notEmpty(componentClasses, "At least one component class must be specified");StartupStep registerComponentClass = getApplicationStartup().start("spring.context.component-classes.register").tag("classes", () -> Arrays.toString(componentClasses));this.reader.register(componentClasses); // 使用BeanDefinition阅读器注册给定class的BeanDefinition registerComponentClass.end();
}

【doRegisterBean】注册给定class的BeanDefinition

private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,@Nullable BeanDefinitionCustomizer[] customizers) {// 实例化 BeanDefinition AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {return;}abd.setAttribute(ConfigurationClassUtils.CANDIDATE_ATTRIBUTE, Boolean.TRUE);abd.setInstanceSupplier(supplier);ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));// 处理公共BeanDefinition注解(包括@Lazy, @Primary, @DependsOn, )AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);if (customizers != null) {for (BeanDefinitionCustomizer customizer : customizers) {customizer.customize(abd);}}// 实例化BeanDefinitionHolder, BeanDefinition持有器 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 注册BeanDefinition BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}
// 注册BeanDefinition, registry实际就是AnnotationConfigApplicationContext(spring容器本身)
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {String beanName = definitionHolder.getBeanName();registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());String[] aliases = definitionHolder.getAliases();if (aliases != null) {String[] var4 = aliases;int var5 = aliases.length;for(int var6 = 0; var6 < var5; ++var6) {String alias = var4[var6];registry.registerAlias(beanName, alias);}}}


【2.3】刷新容器(主要是实例化bean)【重要】

1)调用context.refresh(); 刷新容器;

【AbstractApplicationContext#refresh()】刷新容器

public void refresh() throws BeansException, IllegalStateException {this.startupShutdownLock.lock();try {this.startupShutdownThread = Thread.currentThread();StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// 1 刷新容器的准备工作,包括初始化属性源,并校验属性,保存应用监听器; // Prepare this context for refreshing. prepareRefresh();// 2 默认实现:获取最新容器,包括把容器hashcode赋值给BeanFactory的序列化id// 也可以由子类来刷新内部BeanFactory// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 3 初始化BeanFactory // Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// 4 protected方法,允许子类自行定义BeanFactory的后置处理逻辑 // Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// 5 触发BeanFacotry后置处理器// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// 6 注册Bean后置处理器// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// 7 初始化消息源 ,用于国际化 // Initialize message source for this context.initMessageSource();// 8 初始化应用事件多播器 // Initialize event multicaster for this context.initApplicationEventMulticaster();// 9 子类可以重写的模板方法,以添加特定于上下文的刷新工作。在实例化单例之前,在初始化特殊 bean 时调用;// Initialize other special beans in specific context subclasses.onRefresh();// 10 注册监听器,包括应用事件多播器// Check for listener beans and register them.registerListeners();// 11 实例化所有剩余的单例bean// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// 12 容器刷新的收尾工作,包括清空缓存,注册生命周期处理器并执行其onRefresh(),发布ContextRefreshedEvent事件;// Last step: publish corresponding event.finishRefresh();}catch (RuntimeException | Error ex ) {          // 异常1: 销毁bean// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {contextRefresh.end();}}finally {this.startupShutdownThread = null;this.startupShutdownLock.unlock();}
}


【2.3.1】刷新容器步骤概述

1)刷新容器步骤:

step1)prepareRefresh():刷新容器的准备工作,包括初始化属性源,并校验属性,保存应用监听器;

step2)obtainFreshBeanFactory():获取最新容器,包括把容器hashcode赋值给BeanFactory的序列化id

step3)prepareBeanFactory(beanFactory):初始化BeanFactory,包括设置属性(类加载器,属性编辑器注册器),注册单例(environment,systemProperties系统属性,systemEnvironment环境变量),新增BeanPostProcessor(ApplicationContextAwareProcessor,ApplicationListenerDetector)

step4)postProcessBeanFactory(beanFactory):protected方法,允许子类自行定义BeanFactory的后置处理逻辑;

step5)invokeBeanFactoryPostProcessors(beanFactory):触发BeanFacotry后置处理器,包括触发BeanDefinition注册后置处理器(注册配置类及其方法的BeanDefinition),触发BeanFactory后置处理器(增强配置类,新增Bean后置处理器ImportAwareBeanPostProcessor,新增事件监听器方法处理器EventListenerMethodProcessor)

step6)registerBeanPostProcessors(beanFactory): 注册Bean后置处理器;

step7)initMessageSource(): 初始化消息源,包括注册name=messageSource且类型=DelegatingMessageSource的bean;

step8)initApplicationEventMulticaster(): 初始化应用事件多播器,包括注册name=applicationEventMulticaste且类型=SimpleApplicationEventMulticaster的bean;

step9)onRefresh():可以重写的模板方法,以添加特定于上下文的刷新工作。在实例化单例之前,在初始化特殊 bean 时调用;

step10)registerListeners():注册监听器,包括应用事件多播器(ApplicationEventMulticaster)添加应用监听器(ApplicationListener)及多播应用事件;

step11)finishBeanFactoryInitialization(): 实例化所有剩余的单例bean, 包括新增嵌入值解析器(如PropertySourcesPlaceholderConfigurer),冻结配置信息,实例化单例bean

step12)finishRefresh(): 容器刷新的收尾工作,包括清空缓存,注册生命周期处理器DefaultLifecycleProcessor(name=lifecycleProcessor)并执行其onRefresh(),发布ContextRefreshedEvent事件;

2)刷新容器步骤小结:

  • 初始化BeanFactory,包括设置属性,注册属性单例,注册Bean后置处理器;
  • 触发BeanFactory后置处理器, 包括触发BeanDefinition注册后置处理器以注册BeanDefinition,BeanFactory后置处理器;
  • 注册Bean后置处理器;
  • 初始化MessageSource,用于国际化;
  • 实例化应用事件多播器,应用监听器,用于发布spring启动事件通知订阅者;
  • 根据BeanDefinition实例化bean(最重要)-finishBeanFactoryInitialization()

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【qt】控件1

1.控件使能&#xff08;enabled&#xff09; QPushbutton*stnew QPushbutton(this);//定义一个按钮 st->setEnabled(false);//按钮设置不能使用当设置该控件不能使用的话&#xff0c;对应控件的子控件也不能使用 通过isEnabled()函数可以查看对应控件状态 演示&#xff1…

昇思MindSpore第二课---Transformer

1. Transformer的概念 Transformer是一种基于注意力机制结构的神经网络&#xff0c;其主要的作用就是用于处理机器翻译、语言建模以及文本生成等自然语言的处理。 比如人类在做一篇阅读理解的时候&#xff0c;我们的注意力可能主要集中在我们所阅读的这一行内容。而机器也是如此…

【Go】-bufio库解读

目录 Reader和Writer接口 bufio.Reader/Writer 小结 其他函数-Peek、fill Reader小结 Writer Scanner结构体 缓冲区对于网络数据读写的重要性 Reader和Writer接口 在net/http包生成的Conn 接口的实例中有两个方法叫做Read和Write接口 type Conn interface {Read(b []b…

mac 0S中虚拟机分辨率高怎么办

在VMware Fusion安装的Windows虚拟机有时候会遇到下图的问题&#xff0c;分辨率很高、桌面和任务栏的图标都很小&#xff0c;没办法正常使用。 解决方法&#xff1a; 点击工具栏中的扳手图标&#xff0c;打开设置。 打开系统设置中的“显示器”。 取消勾选“使用Retina全分辨率…

找不到d3dx9_43.dll怎么解决,d3dx9_43.dll缺失的七种解决方法

​在计算机游戏领域&#xff0c;遇到“找不到d3dx9_43.dll”错误信息是一个相当普遍的现象。这一问题不仅影响玩家的游戏体验&#xff0c;还可能导致游戏无法启动或运行不稳定。本文旨在深入解析这一问题的原因&#xff0c;并提供有效的解决方法&#xff0c;帮助广大游戏玩家轻…

论文《基于现实迷宫地形的电脑鼠设计》深度分析(四)——现实迷宫算法

论文概述 《基于现实迷宫地形的电脑鼠设计 》是由吴润强、庹忠曜、刘文杰、项璟晨、孙科学等人于2023年发表的一篇优秀期刊论文。其针对现阶段电脑鼠计算量庞大且不适用于现实迷宫地形的问题&#xff0c;特基于超声波测距与传统迷宫算法原理&#xff0c;设计出一款可在现实…

ARM(安谋) China处理器

0 Preface/Foreword 0.1 参考博客 Cortex-M23/M33与STAR-MC1星辰处理器 ARM China&#xff0c;2018年4月established&#xff0c;独立运行。 1 处理器类型 1.1 周易AIPU 1.2 STAR-MC1&#xff08;星辰处理器&#xff09; STAT-MC1&#xff0c;主要为满足AIOT应用性能、功…

Iview DatePicker 仅允许选择当前月份及以后的月份

iview DatePicker之前月份禁用且下月可用 html代码 <DatePicker type"month" :options"options4" :value"dialogForm.estimatedStartTimeWithCreate" on-change"monthTime($event, loadDateStart)" placeholder"请选择时间&q…

Redis 内存管理

参考&#xff1a;面试官&#xff1a;为什么 Redis 不立刻删除已经过期的数据&#xff1f; 目录 1.Redis 给缓存数据设置过期时间有什么用&#xff1f; 2.Redis 是如何判断数据是否过期的呢&#xff1f; 3.Redis 过期 key 删除策略了解么&#xff1f; 4.大量 key 集中过期怎…

【IC每日一题:SVA简介】

IC每日一题&#xff1a;SVA简介 1 断言概念1.1 断言优势&#xff1b;1.2 断言类型1.2.1 立即断言1.2.2 并行断言1.2.3 并发断言Demo 2 SVA语法2.1 蕴含操作符&#xff1a;|-> 和 ->2.1.1 蕴含操作符 |>2.1.2 蕴含操作符|-> 2.2 延时操作符2.2.1 ##n 操作符 2.3 重复…

深度学习之One Stage目标检测算法2

我们将对单次目标检测器&#xff08;包括SSD系列和YOLO系列等算法&#xff09;进行综述。我们将分析FPN以理解多尺度特征图如何提高准确率&#xff0c;特别是小目标的检测&#xff0c;其在单次检测器中的检测效果通常很差。然后我们将分析Focal loss和RetinaNet&#xff0c;看看…

【MySQL】优化方向+表连接

目录 数据库表连接 表的关系与外键 数据库设计 规范化 反规范化 事务一致性 表优化 索引优化 表结构优化 查询优化 数据库表连接 表的关系与外键 表之间的关系 常见表关系总结 一对一关系&#xff1a;每一条记录在表A中对应表B的唯一一条记录&#xff0c;反之也是&a…

SHELL笔记(概念+变量)

shell 概念 Shell 是一个命令行解释器&#xff0c;它充当用户与操作系统内核之间的桥梁。用户在 Shell 环境下输入各种命令&#xff0c;Shell 负责接收并分析这些命令&#xff0c;然后将其转换为内核能够理解和执行的系统调用。通过这种方式&#xff0c;用户可以便捷地操作计算…

统信UOS开发环境支持Golang

UOS为Golang开发者提供了各种编辑器和工具链的支持,助力开发者实现高质量应用的开发。 文章目录 一、环境部署Golang开发环境安装二、代码示例Golang开发案例三、常见问题1. 包导入错误2. 系统资源限制一、环境部署 Golang开发环境安装 golang开发环境安装步骤如下: 1)安装…

web前端开发--盒子属性

1、设置背景图像固定 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>设置背景图像固定</title><style type"text/css">/*p{background-attachment: scroll;/*fixed固定*//*随元素滚动还是固定*/}&…

Python数据分析NumPy和pandas(三十五、时间序列数据基础)

时间序列数据是许多不同领域的结构化数据的重要形式&#xff0c;例如金融、经济、生态学、神经科学和物理学。在许多时间点重复记录的任何内容都会形成一个时间序列。许多时间序列是固定频率的&#xff0c;也就是说&#xff0c;数据点根据某些规则定期出现&#xff0c;例如每 1…

前端开发之打印功的使用和实例(vue-print-nb)

通过插件来进行实现 前言效果图1、安装插件vue2vue32、 引入Vue项目2、 使用2.1、在项目中创建按钮并且使用v-print绑定绑定打印事件2.2、编写要打印的内容,给内容附加唯一的id2.3、绑定的时间的方法和参数3、整体代码(此代码是通过vue3来进行实现的但是逻辑都是一样的)前言…

使用Web Animations API实现复杂的网页动画效果

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Web Animations API实现复杂的网页动画效果 使用Web Animations API实现复杂的网页动画效果 使用Web Animations API实现复杂…

分享一个mysql-sql优化经验 in (xxx)的优化【 in(集合)改成not in(反集合) 】

一、优化前 如下sql&#xff0c;直接执行时间需要18.341秒 二、优化后 将 in(集合) 改成 not in(反集合)&#xff0c;如下图&#xff0c;执行性能提升至少4倍&#xff0c;需要4.643秒&#xff0c;并且查询结果不变 三、原因分析 为什么速度会变快那么多&#xff1f; in (集…

传感器页面、屏幕刷新任务学习

一、user_SensorPageTask 传感器页任务 ​ /* Private includes -----------------------------------------------------------*/ //includes #include "user_TasksInit.h" #include "user_ScrRenewTask.h" #include "user_SensorPageTask.h" …