0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SpringBoot的嵌入式Web容器是什么时候加载的?

jf_ro2CN3Fa 来源:CSDN 2023-07-11 10:10 次阅读

0.前言

背景:最近有位开发同学说面试被问到Spring Boot 的启动流程,以及被问到Spring Boot 的嵌入式Web容器是什么时候加载的。如何加载的。是怎么无缝切换的。

这些问题,其实回答起来也是比较复杂的。我们今天就从 SpringApplication.run(EasyPaasAdminApplication.class, args);入口,逐渐向下看下执行流程。来试着回答一下前面这两个问题。

后面关于SpringBoot 的web容器可以无缝随意切换为jetty,undertow..这个问题的回答涉及到Spring Boot是如何设计WebServer的。我们后续专门讲解一下。

1. 执行逻辑梳理

一般我们SpringBoot 应用的启动入口都是如下这种固定的写法,

de939068-1f8a-11ee-962d-dac502259ad0.png

也可以是这样

publicstaticvoidmain(String[]args){
SpringApplicationapplication=newSpringApplication(MyApplication.class);
//...customizeapplicationsettingshere
application.run(args)
}

但总之,都是使用SpringApplication 调用静态方法

此方法的注释

Static helper that can be used to run a SpringApplication from the specified source using default settings.

publicstaticConfigurableApplicationContextrun(ClassprimarySource,String...args){
returnrun(newClass[]{primarySource},args);
}

跟过来就到这,可以看到注释运行Spring应用程序,创建并刷新一个新的ApplicationContext。

dece32cc-1f8a-11ee-962d-dac502259ad0.pngdeedf9ea-1f8a-11ee-962d-dac502259ad0.png

跟代码到这儿其实我们对于SpringBoot 的基本启动流程已经知道了。但是要解答什么时候启动的Tomcat 还需要继续分析。

df2549ea-1f8a-11ee-962d-dac502259ad0.png

到这儿我们就可以继续下去,发现Spring Boot 启动WebServer。此处的WebServer我就不展开了,可以点击去就三个方法start ,stop,getPort。可以看出来Spring 在设计接口的时候还是很严谨和精简。

我们的核心脉络是梳理SpringBoot 启动过程,并且回答Tomcat 是如何被启动的。

df49063c-1f8a-11ee-962d-dac502259ad0.png

我们可以看到WebServer 的实现目前内置的有5种。其实Spring Boot 还有一个特性叫做 自动装配。

这就是为什么5个实现,我们最后启动的是Tomcat。此处也不做展开。后面我专门搞一个解析SpringBoot 自动装配的文章。

df7fd20c-1f8a-11ee-962d-dac502259ad0.png

我们看一下内部start 的TomcatWebServer的内部实现。了解过Tomcat 源码的同学看到这儿就基本明白了。

dfa25a34-1f8a-11ee-962d-dac502259ad0.png

好源码跟进过程我们到此结束,我们整理和总结一下。

通过扫一遍源码我们大概可以总结出来如下三个阶段

准备阶段、应用上下文创建阶段、刷新上下文阶段。

准备阶段 :Spring Boot 会加载应用程序的初始设置,并创建 Spring Boot 上下文。这个阶段的核心源码是 SpringApplication 类的 run() 方法,它会调用 Spring Boot 的各个初始化器进行初始化和准备工作。

应用上下文创建阶段 : Spring Boot 会创建应用程序的上下文,包括各种配置信息、Bean 的加载和初始化等。这个阶段的核心源码是 Spring Boot 自动配置机制,通过扫描 classpath 中的配置文件,自动加载和配置各种组件和 Bean。

刷新上下文阶段 :Spring Boot 会执行各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。这个阶段的核心源码是 Spring Boot 的刷新机制,它会调用各种初始化器和监听器,执行各种启动任务。其中启动Tomcat 就是在这个环节进行。

2. 核心源码解析

既然上面我们已经基本上总结除了,Spring Boot的启动脉络。也梳理出了一些核心源码。那么我们对启动过程的核心源码解析一下。

2.1. 准备阶段

在准备阶段中,Spring Boot 会加载应用程序的初始设置,并创建 Spring Boot 上下文。这个阶段的核心源码是 SpringApplication 类的 run() 方法,它会调用 Spring Boot 的各个初始化器进行初始化和准备工作。

publicConfigurableApplicationContextrun(String...args){
//启动计时器
StopWatchstopWatch=newStopWatch();
stopWatch.start();

//定义应用程序上下文和异常报告器列表
ConfigurableApplicationContextcontext=null;
CollectionexceptionReporters=newArrayList<>();

//配置Headless属性
configureHeadlessProperty();

//获取SpringBoot启动监听器
SpringApplicationRunListenerslisteners=getRunListeners(args);
//执行启动监听器的starting方法
listeners.starting();

try{
//解析命令行参数
ApplicationArgumentsapplicationArguments=newDefaultApplicationArguments(args);
//准备应用程序环境
ConfigurableEnvironmentenvironment=prepareEnvironment(listeners,applicationArguments);
//配置忽略BeanInfo
configureIgnoreBeanInfo(environment);
//打印Banner
BannerprintedBanner=printBanner(environment);
//创建应用程序上下文
context=createApplicationContext();
//获取异常报告器,关于异常报告,我下次专门讲一下SpringBoot 的异常收集器。
exceptionReporters=getSpringFactoriesInstances(SpringBootExceptionReporter.class,newClass[]{ConfigurableApplicationContext.class},context);
//准备应用程序上下文
prepareContext(context,environment,listeners,applicationArguments,printedBanner);
//刷新应用程序上下文
refreshContext(context);
//刷新后操作
afterRefresh(context,applicationArguments);
//停止计时器
stopWatch.stop();
//记录启动日志
if(this.logStartupInfo){
newStartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(),stopWatch);
}
//执行启动监听器的started方法
listeners.started(context);
//执行Runner
callRunners(context,applicationArguments);
}catch(Throwableex){
//处理启动失败
handleRunFailure(context,ex,exceptionReporters,listeners);
thrownewIllegalStateException(ex);
}

try{
//执行启动监听器的running方法
listeners.running(context);
}catch(Throwableex){
//处理启动失败
handleRunFailure(context,ex,exceptionReporters,null);
thrownewIllegalStateException(ex);
}

//返回应用程序上下文
returncontext;
}

在 run() 方法中,Spring Boot 首先会创建一个 StopWatch 对象,用于记录整个启动过程的耗时。然后,Spring Boot 会调用 getRunListeners(args) 方法获取 Spring Boot 的各个启动监听器,并调用starting() 方法通知这些监听器启动过程已经开始。接着调用 prepareEnvironment(listeners, applicationArguments) 方法创建应用程序的环境变量。

这个方法会根据用户的配置和默认设置创建一个 ConfigurableEnvironment对象,并将其传给后面的 createApplicationContext() 方法。printBanner(environment) 方法打印启动界面的 Banner,调用 refreshContext(context)方法刷新上下文。这个方法会启动上下文,执行各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会在刷新上下文阶段中进行。

2.2. 应用上下文创建阶段

在应用上下文创建阶段中,Spring Boot 会创建应用程序的上下文,包括各种配置信息、Bean 的加载和初始化等。这个阶段的核心源码是 Spring Boot 自动配置机制,通过扫描 classpath 中的配置文件,自动加载和配置各种组件和 Bean。

protectedConfigurableApplicationContextcreateApplicationContext(){
ClasscontextClass=this.applicationContextClass;
if(contextClass==null){
try{
switch(this.webApplicationType){
caseSERVLET:
contextClass=Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
caseREACTIVE:
contextClass=Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass=Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch(ClassNotFoundExceptionex){
thrownewIllegalStateException(
"UnabletocreateadefaultApplicationContext,"+
"pleasespecifyanApplicationContextClass",ex);
}
}
return(ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

在 createApplicationContext() 方法中,Spring Boot 首先会判断应用程序的类型,如果是 Web 应用程序,则会创建一个 WebApplicationContext;否则,会创建一个普通的 ApplicationContext。调用 BeanUtils.instantiateClass(contextClass) 方法创建应用程序的上下文。这个方法会根据上面的逻辑创建一个相应的 ApplicationContext。调用 load() 方法加载应用程序的配置。

这个方法会扫描 classpath 中的各种配置文件,例如 application.properties、application.yml、META-INF/spring.factories 等,自动配置各种组件和 Bean。调用 postProcessApplicationContext() 方法对应用程序的上下文进行后处理。这个方法会调用各种初始化器和监听器,执行各种初始化任务。

2.3. 刷新上下文阶段

在刷新上下文阶段中,Spring Boot 会执行各种启动任务,包括创建 Web 服务器(刚才我们跟源码的时候也看到了,如上我的截图)、加载应用程序的配置、初始化各种组件等。这个阶段的核心源码是 Spring Boot 的刷新机制,它会调用各种初始化器和监听器,执行各种启动任务。

protectedvoidrefreshContext(ConfigurableApplicationContextapplicationContext){
refresh(applicationContext);
if(this.registerShutdownHook){
try{
applicationContext.registerShutdownHook();
}
catch(AccessControlExceptionex){
//Notallowedinsomeenvironments.
}
}
}

在 refreshContext() 方法中调用 refresh(applicationContext) 方法刷新上下文。这个方法是 ApplicationContext 接口的核心方法,会启动上下文,执行各种启动任务。调用 registerShutdownHook() 方法注册应用程序的关闭钩子。这个方法会在应用程序关闭时自动执行,清理资源、关闭线程等,所以我们利用此特性在服务关闭的时候清理一些资源。并向外部发送告警通知。

在 refresh(applicationContext) 方法中,Spring Boot 会执行上下文的各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会调用各种初始化器和监听器,例如:

for(ApplicationContextInitializerinitializer:getInitializers()){
initializer.initialize(applicationContext);
}

另外,Spring Boot 还会调用各种监听器,我们不做赘述,例如:

for(ApplicationListenerlistener:getApplicationListeners()){
if(listenerinstanceofSmartApplicationListener){
SmartApplicationListenersmartListener=(SmartApplicationListener)listener;
if(smartListener.supportsEventType(eventType)
&&smartListener.supportsSourceType(sourceType)){
invokeListener(smartListener,event);
}
}
elseif(supportsEvent(listener,eventType)){
invokeListener(listener,event);
}
}

基本上就是这些了。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    4983

    文章

    18295

    浏览量

    288591
  • Web服务器
    +关注

    关注

    0

    文章

    137

    浏览量

    24231
  • tomcat
    +关注

    关注

    0

    文章

    27

    浏览量

    4779
  • SpringBoot
    +关注

    关注

    0

    文章

    172

    浏览量

    106

原文标题:三分钟了解 SpringBoot 的启动流程

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式Web访问时的内存丢失的问题怎么解决?

    嵌入式Web在系统中的应用是什么嵌入式Web访问时的内存丢失的问题怎么解决?
    发表于 04-28 06:28

    SpringBoot嵌入式Web容器有哪几种呢

    种类1.servlett web容器,2.reactive web容器spring-boot支持使用jetty、netty、tomcat、undertow作为
    发表于 12-16 06:51

    谈一谈Spring Boot嵌入式Web容器

    Spring Boot嵌入式Web容器Embedded Tomcatorg.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerorg.
    发表于 12-16 08:16

    Spring Boot嵌入式Web容器原理是什么

    Spring Boot嵌入式Web容器原理Spring Boot的目标是构建“非常容易创建、独立、产品级别的基于Spring的应用”。这些应用是“立即可运行的”。在这个过程中,完全没有代码生成
    发表于 12-16 07:57

    如何在嵌入式容器Jetty或Tomcat中运行带有Maven的Java Web应用程序

    在开发Java Web应用程序时,从“真实”环境中获得快速反馈非常实用。 在本文中,我将探讨如何在嵌入式容器Jetty或Tomcat中运行带有Maven的Java Web应用程序。 在
    发表于 12-16 06:24

    SpringBoot嵌入式Servlet容器启动原理是什么

    SpringBoot嵌入式Servlet容器启动原理思维导图
    发表于 12-20 07:26

    嵌入式Servlet容器启动原理是什么

    一、SpringBoot应用启动运行run方法二、run方法调用了refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化
    发表于 12-20 07:54

    SpringBoot应用启动运行run方法

    什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomc
    发表于 12-20 06:16

    嵌入式Servlet容器自动配置原理是什么

    这一节课我们来说一说嵌入式Servlet容器自动配置原理前面我们都知道怎么去配置容器参数,切换容器,但是我们不知道springboot自动配
    发表于 12-20 06:29

    如何配置嵌入式的Servlet容器

    以前的web应用开发我们采取的方式是项目完成后打包成war包,然后配置tomcat启动运行项目,而Spring Boot默认使用的是嵌入式的tomcat,那我们需要如何配置嵌入式的Servlet
    发表于 12-20 07:18

    SpringBoot配置嵌入式Servlet

    SpringBoot配置嵌入式Servlet容器定制和修改Servlet容器相关配置全局配置文件编写WebServerFactoryCustomizer注册Servlet三大组件注册S
    发表于 12-20 06:19

    什么时候获取嵌入式的Servlet容器并启动Tomcat

    什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomc
    发表于 12-20 06:11

    如何对嵌入式Servlet容器进行配置呢

    一、配置嵌入式Servlet容器SpringBoot默认使用Tomcat作为嵌入式的Servlet容器;1、定制和修改Servlet
    发表于 12-21 06:09

    嵌入式Servlet容器启动原理

    SpringBoot源码学习系列之嵌入式Servlet容器启动原理SpringBoot的自动配置就是SpringBoot的精髓所在,对于
    发表于 12-22 07:23

    如何配置嵌入式Servlet容器

    springboot默认使用的是嵌入式的servlet容器(tomcat):定制和修改servlet容器的相关配置1)修改和server有关的配置(ServerProperties类
    发表于 12-24 06:56