SpringBoot运行流程源码分析

run方法核心流程

我们在启动SpringBoot的时候调用的是SpringApplication类的静态run方法。其核心流程如下图所示:
在这里插入图片描述
在run方法内完成了SpringApplication的声明周期。,这个过程涉及的几个核心类如下:
SpringApplicationRunListeners:这个接口会在run方法执行的过程中被调用,也就是可以看做SpringApplication的生命周期回调函数,比如ApplicationCntext创建好了可以调用contextPrepared方法,该方法传入的参数是ConfigurableApplicationContext,也就是spring的容器,如果我们有某些业务要在应用启动前进行处理都可以在这个接口的回调中实现。

较新版本的run方法源码如下:

/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {Startup startup = Startup.create();if (this.registerShutdownHook) {SpringApplication.shutdownHook.enableShutdownHookAddition();}DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();//从META-INF/spring.factories配置文件中加载ListenersSpringApplicationRunListeners listeners = getRunListeners(args);//启动listeners的starting回调方法listeners.starting(bootstrapContext, this.mainApplicationClass);try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//创建环境变量管理类,ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);//打印Springboot的启动画面Banner printedBanner = printBanner(environment);//创建spring容器context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);//准备spring容器,也就是把环境啥的给spring容器,完成bean定义的加载等功能prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//刷新spring容器,即开启bean的创建过程(主要是单例bean?)refreshContext(context);//这个函数目前实现为空,留作后面扩展afterRefresh(context, applicationArguments);startup.started();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);}//调用listeners的started回调函数listeners.started(context, startup.timeTakenToStarted());//调用Runners的实现类,就是在springboot启动后可以实现一些业务处理,比如你的应用可能要升级,做一些升级操作都可以在Runners实现类中完成callRunners(context, applicationArguments);}catch (Throwable ex) {throw handleRunFailure(context, ex, listeners);}try {if (context.isRunning()) {listeners.ready(context, startup.ready());}}catch (Throwable ex) {throw handleRunFailure(context, ex, null);}return context;}

SpringApplicationRunListener监听器

如上所述,这个监听器就是在run方法的执行过程中,会被调用其回调函数的,SpringBoot官方只注册了一个Listener,即EventPublishingRunListener,Listeners都是配置到META-INF/spring.factories配置文件中的。

SpringApplicationRunListener源码

public interface SpringApplicationListener {//在上面的run方法里已看到过有调用,在比较前的位置就调用了default void starting(){};/** environment准备完成,在ApplicationContext创建之前,该方法被调用* Called once the environment has been prepared, but before the* {@link ApplicationContext} has been created.* @param bootstrapContext the bootstrap context* @param environment the environment*/default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {}/*** Called once the {@link ApplicationContext} has been created and prepared, but* before sources have been loaded.* @param context the application context*/default void contextPrepared(ConfigurableApplicationContext context) {}/*** Called once the application context has been loaded but before it has been* refreshed.* @param context the application context*/default void contextLoaded(ConfigurableApplicationContext context) {}/*** The context has been refreshed and the application has started but* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner* ApplicationRunners} have not been called.* @param context the application context.* @param timeTaken the time taken to start the application or {@code null} if unknown* @since 2.6.0*/default void started(ConfigurableApplicationContext context, Duration timeTaken) {}/*** Called immediately before the run method finishes, when the application context has* been refreshed and all {@link CommandLineRunner CommandLineRunners} and* {@link ApplicationRunner ApplicationRunners} have been called.* @param context the application context.* @param timeTaken the time taken for the application to be ready or {@code null} if* unknown* @since 2.6.0*/default void ready(ConfigurableApplicationContext context, Duration timeTaken) {}/*** Called when a failure occurs when running the application.* @param context the application context or {@code null} if a failure occurred before* the context was created* @param exception the failure* @since 2.0.0*/default void failed(ConfigurableApplicationContext context, Throwable exception) {}
}

官方代码的注释已比较清楚了。总结如下图:
在这里插入图片描述
我们可以自定义SpringApplicationRunListener以实现一些在spring应用启动过程中需要完成的业务逻辑。

初始化ApplicationArguments

这个对象主要用来封装main方法的参数args

初始化ConfigurableEnvironment

ConfiguraableEnvironment接口继承自Environment接口和ConfigurablePropertyResolver接口,主要作用是提供当前运行环境的公开接口,比如配置文件profiles各类系统属性和变量的设置、添加、读取、合并等功能。

源码如下:

public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {//设置激活的组集合void setActiveProfiles(String... profiles);//向当前激活的组集合中添加一个profilevoid addActiveProfile(String profile);//设置默认激活的组集合,激活的组集合为空时会使用默认的组集合void setDefaultProfiles(String... profiles);//获取当前环境对象中的属性源集合MutablePropertySources getPropertySources();//获取虚拟机环境变量,该方法提供了直接配置虚拟机环境变量的入口Map<String, Object> getSystemProperties();//获取操作系统的环境变量Map<String, Object> getSystemEnvironment();//合并指定环境中的配置到当前环境中void merge(ConfigurableEnvironment parent);
}

打印Banner

就是在控制台打印Banner,可以自定义自己要打印的Logo。

Spring应用上下文的创建

应用上下文的创建会根据推断出来的web应用类型,创建不同的应用上下文。springboot中使用如下默认的应用程序上下文工厂创建:

/** Copyright 2012-2022 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.boot;import java.util.function.BiFunction;
import java.util.function.Supplier;import org.springframework.aot.AotDetector;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;/*** Default {@link ApplicationContextFactory} implementation that will create an* appropriate context for the {@link WebApplicationType}.** @author Phillip Webb*/
class DefaultApplicationContextFactory implements ApplicationContextFactory {@Overridepublic Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {return getFromSpringFactories(webApplicationType, ApplicationContextFactory::getEnvironmentType, null);}@Overridepublic ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {return getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, null);}@Overridepublic ConfigurableApplicationContext create(WebApplicationType webApplicationType) {try {return getFromSpringFactories(webApplicationType, ApplicationContextFactory::create,this::createDefaultApplicationContext);}catch (Exception ex) {throw new IllegalStateException("Unable create a default ApplicationContext instance, "+ "you may need a custom ApplicationContextFactory", ex);}}private ConfigurableApplicationContext createDefaultApplicationContext() {if (!AotDetector.useGeneratedArtifacts()) {return new AnnotationConfigApplicationContext();}return new GenericApplicationContext();}private <T> T getFromSpringFactories(WebApplicationType webApplicationType,BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class,getClass().getClassLoader())) {T result = action.apply(candidate, webApplicationType);if (result != null) {return result;}}return (defaultResult != null) ? defaultResult.get() : null;}}

由源码可以看到需要根据推断出来的应用类型创建上下文对象。

Spring应用上下文的准备

应用上下文准备阶段

主要由三步:
对context设置environment,在AnnotationConfigServletWebServerApplicationContext中的实现如下:

/*** {@inheritDoc}* <p>* Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader} and* {@link ClassPathBeanDefinitionScanner} members.*/@Overridepublic void setEnvironment(ConfigurableEnvironment environment) {super.setEnvironment(environment);this.reader.setEnvironment(environment);this.scanner.setEnvironment(environment);}

主要是将环境对象设置给reader和scanner

应用上下文后置处理器:
给上下文对象持有的beanFactory容器设置beanNameGenerator,设置资源加载器和类加载器。ConversionService的设置。

ApplicationContextInitializer初始化context操作:ApplicationContextInitializer接口允许在应用上下文初始化之前(还没有调用refresh刷新应用上下文)执行一些自定义逻辑,需要实现其initialize方法,在这一步会被调用。

应用上下文加载阶段

主要实现对bean定义的加载,在以下方法内:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);postProcessApplicationContext(context);addAotGeneratedInitializerIfNecessary(this.initializers);applyInitializers(context);listeners.contextPrepared(context);bootstrapContext.close(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}if (this.keepAlive) {context.addApplicationListener(new KeepAlive());}context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));if (!AotDetector.useGeneratedArtifacts()) {// Load the sources 加载所有配资源Set<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");//调用加载bean定义的方法load(context, sources.toArray(new Object[0]));}listeners.contextLoaded(context);}

这个加载bean定义的核心逻辑在spring源码中,核心实现是在BeanDefinitionLoader中
其构造函数如下:

/*** Create a new {@link BeanDefinitionLoader} that will load beans into the specified* {@link BeanDefinitionRegistry}.* @param registry the bean definition registry that will contain the loaded beans* @param sources the bean sources*/BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {Assert.notNull(registry, "Registry must not be null");Assert.notEmpty(sources, "Sources must not be empty");this.sources = sources;this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);this.xmlReader = new XmlBeanDefinitionReader(registry);this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);this.scanner = new ClassPathBeanDefinitionScanner(registry);this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));}

由构造函数可知,这个加载bean定义的类支持多种加载方法,由于目前的sources来源是primarySources配置源和sources配置源,这两个变量的类型分别为Class和String,所以实际上只加载类和字符配置源。我们经常用到的应该是基于java类的注解配置,所以一般是调用下面这个方法:

private void load(Class<?> source) {if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {// Any GroovyLoaders added in beans{} DSL can contribute beans hereGroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());}if (isEligible(source)) {this.annotatedReader.register(source);}}

即基于注解的配置类加载过程。

Spring应用上下文的刷新

即调用AbstractApplicationContext的refresh方法,这是spring的功能,在spring-context包内,属于初始化SpringIOC容器的过程。简要代码如下:

public void refresh() throws BeansException, IllegalStateException{prepareRefresh();//通知子类刷新内部bean工厂ConfigurableListableBeanFDactory beanFactory = obtainFreshBeanFactory();//为当前context准备bean工厂prepareBeanFactory(beanFactory);try {//允许context的子类对bean工厂进行后置处理postProcessBeanFactory(beanFactory);//调用context中注册为bean的工厂处理器invokeBeanFactoryPostProcessors(beanFactory);//注册bean处理器(beanPostProcessors)registerBeanPostProcessors(beanFactory);//初始化context的信息源,和国际化有关initMessageSource();//初始化context的事件传播器initApplicationEventMulticaster();//初始化其他特殊的beanonRefresh();//检查并注册事件监听器registerListeners();//实例化所有非懒加载单例finishBeanFactoryInitialization(beanFactory);//最后一步:发布对应事件finishRefresh();  } catch (BeanException e ) {} finally {}}

调用ApplicationRunner和CommandLineRunner

即获取上一步中注册在spring容器内的ApplicationRunner和CommandLineRunner实现,对他们进行排序(基于@Order控制执行顺序),然后一个一个调用。所以这两个接口可以在SpringBoot初始化完成之后执行一些处理逻辑。

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

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

相关文章

一起学Java(1)-新建一个Gradle管理的Java项目

一时兴起&#xff0c;也为了便于跟大家同步学习进展和分享样例代码&#xff0c;遂决定创建一个全新的Java项目&#xff0c;并通过Github与大家分享。本文就是记录该项目的创建过程以及其中的一些知识要点&#xff08;如Gradle等&#xff09;。为了紧跟技术潮流和提高操作效率&a…

鱼哥好书分享活动第28期:看完这篇《终端安全运营》终端安全企业基石,为你的终端安全保驾护航!

鱼哥好书分享活动第28期&#xff1a;看完这篇《终端安全运营》终端安全企业基石&#xff0c;为你的终端安全保驾护航&#xff01; 读者对象&#xff1a;主要内容&#xff1a;本书目录&#xff1a;了解更多&#xff1a;赠书抽奖规则: 在当前网络威胁日益复杂化的背景下&#xff…

linux nginx 命令记录,和转发

nginx: 查看配置文件&#xff1a;sudo find / -name nginx.conf 配置文件&#xff1a;/etc/nginx/nginx.conf 检查nginx.conf文件正确性 nginx -t -c /path/to/nginx.conf 或者 有nginx命令执行 nginx -t 查找nginx 可执行文件&#xff1a;which nginx /usr/sbin/nginx 安装Ng…

游戏UI设计大师课:3款游戏 UI 设计模板

很多时候&#xff0c;做设计需要找素材。假如是普通的 UI 界面或者 Banner 等等&#xff0c;在Dribbble、Pinterest、即时设计、Behance 翻看这样的网站&#xff0c;至少可以梳理出一些想法和思路。如果你需要一个更规范的指南&#xff0c;此时&#xff0c;在各种设计规范、官方…

.Net 检验信息采集及管理系统LIS,成熟的医院实验室管理系统源码

检验管理系统LIS实现了检验信息电子化、检验信息管理自动化&#xff0c;具备与医嘱双向沟通、采用条码管理手段、财务自动计费、仪器双向控制等重要功能特点。其工作流程为通过门诊医生和住院工作站提出检验申请&#xff0c;生成相应患者的化验条码标签&#xff0c;在生成化验单…

根据题意写出完整的css,html和js代码【购物车模块页面及功能实现】

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 问题描述 根据题意写出完…

从煎饼摊到便利店,如何理解公司的商业价值 - 《进阶吧!投资者4》读后感

投资逻辑永远要遵循最基本的商业逻辑。要搞清楚两个最基本问题&#xff0c;认知一家企业最基本的要素是什么&#xff1b;盈利的本质是什么。 评估公司的商业价值 要评估企业的估值&#xff0c;我们可以在战术上用资产未来可以产生现金流的折现来评估资产的价值&#xff0c;如果…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 亲子游戏(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

Linux_权限3

Linux所对应的文件类型 1.在Win下&#xff0c;有文件类型&#xff0c;通常通过后缀标识 日常用的就是windows系统这里不做举例. 2.Linux的文件类型不通过后缀区分&#xff08;不代表Linux不用后缀) 其中需要注意的是第一个字符表示文件类型的含义 - :普通文件, 文本, 源代码…

2000-2022年上市公司党建情况:企业设立党组织数据概览

上市公司党建企业设立党组织数据&#xff1a;解析企业党组织建设的关键指标 上市公司企业党组织是党在企业中的基层组织&#xff0c;根据企业中党员的数量&#xff0c;经过上级党组织的批准&#xff0c;可以设立不同级别的党组织。这些党组织在企业中发挥着重要的政治核心作用…

Nginx核心功能(反向代理/负载均衡/动静分离)和搭建HA高可用

1. nginx的核心功能 1.1 nginx反向代理功能 正向代理 代理的为客户端&#xff0c;对于服务器不知道真实客户的信息。例如:翻墙软件。 反向代理 反向代理&#xff08;Reverse Proxy&#xff09;是一种服务器配置&#xff0c;它位于客户端和服务器之间&#xff0c;充当客户端请…

开源数据结构存储系统Redis的内部数据结构详解(上)

目录 1、简单动态字符串 1.1、SDS的定义 1.2、SDS与C字符串的区别 2、链表 2.1、链表的定义 2.2、特性 3、字典 3.1、哈希表定义 3.2、哈希表节点定义 3.3、字典定义 3.4、Rehash 3.5、渐进式rehash 4、总结 C++软件异常排查从入门到精通系列教程(专栏文章列表,…

C#基础——类、构造函数和静态成员

类 类是一个数据类型的蓝图。构成类的方法和变量称为类的成员&#xff0c;对象是类的实例。类的定义规定了类的对象由什么组成及在这个对象上可执行什么操作。 class 类名 { (访问属性) 成员变量; (访问属性) 成员函数; } 访问属性&#xff1a;public&#xff08;公有的&…

33.【C语言】实践扫雷游戏

预备知识&#xff1a; 第13篇 一维数组 第13.5篇 二维数组 第28篇 库函数 第29篇 自定义函数 第30篇 函数补充 0x1游戏的运行&#xff1a; 1.随机布置雷 2.排雷 基本规则&#xff1a; 点开一个格子后&#xff0c;显示1&#xff0c;对于9*9&#xff0c;代表以1为中心的去…

【五】架构设计之思考路线

架构设计之思考路线 概述 看过不少本架构设计方面的书籍&#xff0c;如《亿级流量网站架构核心技术》《超大流量分布式系统架构解决方案》《企业IT架构转型之道》《从程序员到架构师》等&#xff0c;看完之后最终发现架构设计思维大同小异&#xff0c;无非都是围绕实现三高&…

学习Java的日子 Day56 数据库连接池,Druid连接池

Day56 1.数据库连接池 理解&#xff1a;池就是容器&#xff0c;容器中存放了多个连接对象 使用原因&#xff1a; 1.优化创建和销毁连接的时间&#xff08;在项目启动时创建连接池&#xff0c;项目销毁时关闭连接池&#xff09; 2.提高连接对象的复用率 3.有效控制项目中连接的…

Windows下Pytorch入门深度学习环境安装与配置(CPU版本)

Windows下Pytorch入门深度学习环境安装与配置&#xff08;CPU版本&#xff09; 一、安装过程中各个软件的作用&#xff08;一&#xff09;Python&#xff08;二&#xff09;库 / 包 / package / library&#xff08;三&#xff09;PyTorch / Tensorflow&#xff08;四&#xff…

Java之开发 系统设计 分布式 高性能 高可用

1、restful api 基于rest构建的api 规范&#xff1a; post delete put get 增删改查路径 接口命名 过滤信息状态码 2、软件开发流程 3、命名规范 类名&#xff1a;大驼峰方法名&#xff1a;小驼峰成员变量、局部变量&#xff1a;小驼峰测试方法名&#xff1a;蛇形命名 下划…

【云原生】Docker搭建知识库文档协作平台Confluence

目录 一、前言 二、企业级知识库文档工具部署形式 2.1 开源工具平台 2.1.1 开源工具优点 2.1.2 开源工具缺点 2.2 私有化部署 2.3 混合部署 三、如何选择合适的知识库平台工具 3.1 明确目标和需求 3.2 选择合适的知识库平台工具 四、Confluence介绍 4.2 confluence特…

平面点云三角化边数与点的关系

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 点云三角化定义 原文 说人话&#xff1a; 一个二维平面点集P三角化结果是一个满足以下条件的三角形集合&#xff1a; 1 所有三角形的并集刚好是P的凸包。 2 所有三角…