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

news2024/11/19 18:17:04

文章目录

  • 【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 构造器");
    }
    @Bean
    public 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.coloradmin.cn/o/2243545.html

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

相关文章

使用PSpice进行第一个电路的仿真

1、单击【开始】菜单&#xff0c;选择【OrCAD Capture CIS Lite】。 2、单击【File】>【New】>【Project】。 3、①填入Name下面的文本框&#xff08;提示&#xff1a;项目名称不要出现汉字&#xff09;&#xff1b; ②选择【Analog or Mixed A/D】&#xff1b; ③单击【…

CentOS网络配置

上一篇文章&#xff1a;VMware Workstation安装Centos系统 在CentOS系统中进行网络配置是确保系统能够顺畅接入网络的重要步骤。本文将详细介绍如何配置静态IP地址、网关、DNS等关键网络参数&#xff0c;以帮助需要的人快速掌握CentOS网络配置的基本方法和技巧。通过遵循本文的…

【unity小技巧】unity最全的性能优化方案分享以及如何进行性能测试(2024/11/11更新)

文章目录 前言一、性能分析软件1、Draw Call什么是Draw Call如何查看Draw Call数量 2、分析帧调试器3、Statistics统计面板 二、优化手段1、关于图集、材质、层级的处理&#xff0c;减少DrawCall2、批处理3、音乐处理4、减少沉余资源和重复资源5、渲染优化&#xff08;GPU&…

[STM32]从零开始的STM32 HAL库环境搭建

一、前言 之前在搭建STM32的标准库环境时就告诉过大家&#xff0c;开发STM32的方式主要有三种。一种是最原始但是效率最高的寄存器开发&#xff0c;另一种是效率仅次于寄存器难度相对较低的标准库开发&#xff0c;最后一种是最为简单但是程序效率最低的HAL库开发。如果对于初学…

阅读2020-2023年《国外军用无人机装备技术发展综述》笔记_技术趋势

目录 文献基本信息 序言 1 发展概况 2 重点技术发展 2.1 人工智能技术 2.1.1 应用深化 2.1.2 作战效能提升 2.2 航空技术 2.2.1螺旋桨设计创新 2.2.2 发射回收技术进步 2.3 其他相关技术 2.3.1 远程控制技术探 2.3.2 云地控制平台应用 3 装备系统进展 3.1 无人作…

python爬虫(二)爬取国家博物馆的信息

import requests from bs4 import BeautifulSoup# 起始网址 url https://www.chnmuseum.cn/zx/xingnew/index_1.shtml # 用于存储所有数据 all_data [] page 1 global_index 1 # 定义全局序号变量并初始化为1 while True:html_url requests.get(url).textif requests.get…

FPGA 第6讲 简单组合逻辑多路选择器

时间&#xff1a;2024.11.11-11.14 一、学习内容 1.组合逻辑 组合逻辑是VerilgHDL设计中一个重要组成部分。从电路本质上讲&#xff0c;组合逻辑电路的特点是输出信号只是当前时刻输入信号的函数&#xff0c;与其他时刻的输入状态无关&#xff0c;无存储电路&#xff0c;也没…

性能超越Spark 13.3 倍,比某MPP整体快数十秒 | 多项性能指标数倍于主流开源引擎 | 云器科技发布性能测试报告

云器Lakehouse正式发布性能测试报告 &#x1f3c5;离线批处理&#xff1a;在复杂批处理任务中&#xff0c;云器Lakehouse相较Spark表现出13.31倍性能提升。 &#x1f3c5;即席查询&#xff1a;在交互式分析场景下&#xff0c;云器Lakehouse相较Trino表现出9.84倍性能提升。 &am…

Frida反调试对抗系列(四)百度加固

本文只是交流技术&#xff0c;如有侵权请联系我删除。 知识星球&#xff1a;https://t.zsxq.com/kNlj4 前言&#xff1a; 上一篇文章我们提到 我们使用github开源魔改好的frida server 但是仍然有一些厂商的server不能通过&#xff0c;那么这篇文章针对百度加固 进行快速通…

利用redis的key失效监听器KeyExpirationEventMessageListener作任务定时提醒功能

某需求&#xff1a; 要求在任务截止日期的前3天时&#xff0c;系统自动给用户发一条消息提醒。 用定时任务的话感觉很不舒服。间隔时间不好弄。不能精准卡到那个点。 由于系统简单&#xff0c;没有使用消息列队&#xff0c;也不能使用延时队列来做。 用Timer的话开销还挺大的&a…

通过MongoDB Atlas 实现语义搜索与 RAG——迈向AI的搜索机制

目录 通过MongoDB Atlas 实现语义搜索与 RAG——迈向AI的搜索机制 一、引言 二、语义搜索与 MongoDB Atlas 的背景 三、MongoDB Atlas 的向量搜索功能 1. 向量搜索的实现方式 2. 典型操作示例 四、RAG 在 MongoDB Atlas 的应用 1、RAG是什么 2、RAG 的实现过程 3、RA…

【graphics】图形绘制 C++

众所周知&#xff0c;周知所众&#xff0c;图形绘制对于竞赛学僧毫无用处&#xff0c;所以这个文章&#xff0c;专门对相关人员教学&#xff08;成长中的码农、高中僧、大学僧&#xff09;。 他人经验教学参考https://blog.csdn.net/qq_46107892/article/details/133386358?o…

Excel使用-弹窗“此工作簿包含到一个或多个可能不安全的外部源的链接”的发生与处理

文章目录 前言一、探讨问题发生原因1.引入外部公式2.引入外部数据验证二、问题现象排查及解决1.排查公式2.排查数据验证3.特殊处理方式总结前言 作为一种常用的办公软件,Excel被大家所熟知。尽管使用了多年,有时候在使用Excel时候也会发生一些不太常见的现象,需要用心核查下…

递归(3)----力扣40组合数2,力扣473火柴拼正方形

给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 示例 1: 输入: candidates [10,1,2,7,6,1…

CCF-第七届AIOps国际挑战赛-季军方案分享|北航-EasyRAG

历经4个月的时间&#xff0c;从初赛赛道第1&#xff0c;复赛赛道第2&#xff0c;到最后决赛获得季军&#xff0c;这一路我们团队收获了很多实践经验&#xff0c;也结识了不少业界的RAG研究者&#xff0c;受益匪浅。应组委会邀请&#xff0c;本文介绍一下我们EasyRAG方案的亮点和…

Spring Boot出现java: 错误: 无效的源发行版:16的解决方式

第一步&#xff1a; 修改为SDK的目标字节码版本 第二步&#xff1a;CtrlShiftAltS进入项目结构 第三步&#xff1a;pom.xml文件中 在网上搜索和自己SDK适配的Springboot版本&#xff0c;1.8对应的是2.7.1&#xff08;可以用&#xff09; 修改Java版本为1.8 最后的最后&a…

Redis学习 ——缓存

文章目录 一、Redis缓存的介绍二、Redis缓存问题2.1 缓存穿透2.2 缓存击穿2.3 缓存雪崩2.4 双写一致性2.5 缓存持久化RDBAOF 三、缓存数据管理3.1 数据过期策略3.2 数据淘汰策略 一、Redis缓存的介绍 我们在日常的代码编写中比较少使用到Redis&#xff0c;但是如果涉及到了比较…

【开源免费】基于Vue和SpringBoot的在线考试系统(附论文)

本文项目编号 T 624 &#xff0c;文末自助获取源码 \color{red}{T624&#xff0c;文末自助获取源码} T624&#xff0c;文末自助获取源码 网络的广泛应用给生活带来了十分的便利。所以把在线考试管理与现在网络相结合&#xff0c;利用java技术建设在线考试系统&#xff0c;实现…

git-.git目录解析

目录 .git目录下的文件信息 logs&#xff1a;记录各个分支日志记录 refs&#xff1a;记录本地分支、远程分支、tag标签最新commitID config: 配置信息&#xff0c;详细内容解析看下面介绍HEAD: 工作空间当前所在分支 inde文件: 它又常被称为“暂存区”或“缓存区”。这个文件…

sglang 部署Qwen2VL7B,大模型部署,速度测试,深度学习

sglang 项目github仓库&#xff1a; https://github.com/sgl-project/sglang 项目说明书&#xff1a; https://sgl-project.github.io/start/install.html 资讯&#xff1a; https://github.com/sgl-project/sgl-learning-materials?tabreadme-ov-file#the-first-sglang…