亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Springboot啟動(dòng)原理詳細(xì)講解

 更新時(shí)間:2022年07月18日 10:27:08   作者:bijian-bijian  
這篇文章主要介紹了SpringBoot啟動(dòng)原理的分析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

主啟動(dòng)類方法:

@SpringBootApplication
public class MyJavaTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyJavaTestApplication.class, args);
    }
}

點(diǎn)擊進(jìn)入方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

先看看new SpringApplication(primarySources)里做了什么?

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判斷當(dāng)前web容器的類型,一般返回SERVLET,標(biāo)識(shí)是web類型
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
//獲取META-INF/spring.factories文件中以//org.springframework.boot.Bootstrapper和
//org.springframework.boot.BootstrapRegistryInitializer為key的class
//創(chuàng)建對(duì)象,然后裝入對(duì)象屬性中;
   this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
   setInitializers((Collection) 
//獲取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationContextInitializer的key的對(duì)
//存入到initializers集合屬性中;
getSpringFactoriesInstances(ApplicationContextInitializer.class));
//獲取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationListener的key的對(duì)
//存入到listeners集合屬性中;
   setListeners((Collection) 
getSpringFactoriesInstances(ApplicationListener.class));
//找到main方法的啟動(dòng)主類class對(duì)象賦值到對(duì)象屬性中。
   this.mainApplicationClass = deduceMainApplicationClass();
}

spring.factories讀取了對(duì)外擴(kuò)展的ApplicationContextInitializer ,ApplicationListener 對(duì)外擴(kuò)展, 對(duì)類解耦(比如全局配置文件、熱部署插件)

所以我們可以利用這一特性,在容器加載的各個(gè)階段進(jìn)行擴(kuò)展。

上面讀取到的對(duì)象getSpringFactoriesInstances(ApplicationContextInitializer.class))

存入到initializers集合中的對(duì)象

getSpringFactoriesInstances(ApplicationListener.class));存入到listeners集合屬性中的對(duì)象

再看一下最重要的run方法:

public ConfigurableApplicationContext run(String... args) {
// 用來(lái)記錄當(dāng)前springboot啟動(dòng)耗時(shí) 
   StopWatch stopWatch = new StopWatch();
// 就是記錄了啟動(dòng)開始時(shí)間 
   stopWatch.start();
//創(chuàng)建DefaultBootstrapContext bootstrapContext = new 
//DefaultBootstrapContext();對(duì)象,然后循環(huán)執(zhí)行上面文件中賦值的bootstrapRegistryInitializers集合中對(duì)象的方法,入?yún)⒕褪莃ootstrapContext對(duì)象,利用此處我們可以擴(kuò)展改變bootstrapContext對(duì)象中的一項(xiàng)屬性值等,在這里還不知道此bootstrapContext對(duì)象的用處。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext實(shí)現(xiàn) 
   ConfigurableApplicationContext context = null;
// 開啟了Headless模式,暫時(shí)不知道此模式的作用 
   configureHeadlessProperty();
//去spring.factroies中讀取了 org.springframework.boot.SpringApplicationRunListener為key的對(duì)象,默認(rèn)是EventPublishingRunListener對(duì)象,然后封裝進(jìn)SpringApplicationRunListeners 對(duì)象中,此對(duì)象還是比較有用的,用來(lái)發(fā)布springboot啟動(dòng)進(jìn)行中的各個(gè)狀態(tài)的事件,上面方法中讀取到的監(jiān)聽器就可以監(jiān)聽到這些事件,所以可以運(yùn)用這些特性進(jìn)行自己的擴(kuò)展。
   SpringApplicationRunListeners listeners = getRunListeners(args);
// 發(fā)布1.ApplicationStartingEvent事件,在運(yùn)行開始時(shí)發(fā)送 ,發(fā)送springboot啟動(dòng)開始事件;
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
//根據(jù)啟動(dòng)項(xiàng)目命令所帶的參數(shù)創(chuàng)建applicationArguments 對(duì)象
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//初始化環(huán)境變量:讀取系統(tǒng)環(huán)境變量、發(fā)送了一個(gè)ApplicationEnvironmentPreparedEvent事件,利用相關(guān)監(jiān)聽器來(lái)解析項(xiàng)目中配置文件中的配置,(EnvironmentPostProcessorApplicationListener
監(jiān)聽器解析的配置文件)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 忽略beaninfo的bean 
      configureIgnoreBeanInfo(environment);
// 打印Banner 橫幅 
      Banner printedBanner = printBanner(environment);
//根據(jù)webApplicationType 容器類型,創(chuàng)建對(duì)應(yīng)的spring上下文,一般是AnnotationConfigServletWebServerApplicationContext;
      context = createApplicationContext();
//給spring上下文賦值DefaultApplicationStartup對(duì)象
      context.setApplicationStartup(this.applicationStartup);
//預(yù)初始化上下文,這里做了給上下文添加environment對(duì)象,上面獲取到的initializers集合中的ApplicationContextInitializer對(duì)象執(zhí)行其入?yún)樯舷挛膇nitialize方法,對(duì)上下文做編輯,所以此處我們可以做擴(kuò)展;發(fā)送ApplicationContextInitializedEvent容器初始化事件;發(fā)送BootstrapContextClosedEvent事件;最重要的一個(gè)方法就是把啟動(dòng)配置類注冊(cè)成了beanDefinition;發(fā)送ApplicationPreparedEvent事件,并把listeners集合屬性中的事件添加到上下文中;
	prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
	//刷新容器:和spring中的refresh()方法的作用是一樣的,主要的作用就是讀取所有的bean轉(zhuǎn)成beanDefinition然后再創(chuàng)建bean對(duì)象;不過這個(gè)容器重寫了其中的onRefresh()方法,在此方法中,創(chuàng)建了springboot內(nèi)置的tomcat對(duì)象并進(jìn)行啟動(dòng);接下來(lái)特別說明一下這個(gè)內(nèi)置tomcat
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
//打印啟動(dòng)時(shí)間;
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
//發(fā)布容器啟動(dòng)事件ApplicationStartedEvent;發(fā)布AvailabilityChangeEvent容器可用實(shí)踐;
      listeners.started(context);
//執(zhí)行容器中ApplicationRunner、ApplicationRunner類型對(duì)象的run方法
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
//發(fā)布ApplicationReadyEvent容器已經(jīng)正常事件;
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

prepareEnvironment方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // 根據(jù)webApplicationType 創(chuàng)建Environment  創(chuàng)建就會(huì)讀取: java環(huán)境變量和系統(tǒng)環(huán)境變量
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 將命令行參數(shù)讀取環(huán)境變量中
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 將@PropertieSource的配置信息 放在第一位, 因?yàn)樽x取配置文件@PropertieSource優(yōu)先級(jí)是最低的
   ConfigurationPropertySources.attach(environment);
   // 發(fā)布了ApplicationEnvironmentPreparedEvent 的監(jiān)聽器  讀取了全局配置文件
   listeners.environmentPrepared(environment);
   // 將所有spring.main 開頭的配置信息綁定SpringApplication
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   //更新PropertySources
   ConfigurationPropertySources.attach(environment);
   return environment;
}

lprepareContext

l預(yù)初始化上下文

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   // 拿到之前讀取到所有ApplicationContextInitializer的組件, 循環(huán)調(diào)用initialize方法
   applyInitializers(context);
   // 發(fā)布了ApplicationContextInitializedEvent
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // 獲取當(dāng)前spring上下文beanFactory (負(fù)責(zé)創(chuàng)建bean)
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   // 在Spring下 如果出現(xiàn)2個(gè)重名的bean, 則后讀取到的會(huì)覆蓋前面
   // 在SpringBoot 在這里設(shè)置了不允許覆蓋, 當(dāng)出現(xiàn)2個(gè)重名的bean 會(huì)拋出異常
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // 設(shè)置當(dāng)前spring容器是不是要將所有的bean設(shè)置為懶加載
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   // 讀取主啟動(dòng)類,將它注冊(cè)為BD、就像我們以前register(啟動(dòng)類);一個(gè)意思 (因?yàn)楹罄m(xù)要根據(jù)配置類解析配置的所有bean)
   load(context, sources.toArray(new Object[0]));
   //4.讀取完配置類后發(fā)送ApplicationPreparedEvent。
   listeners.contextLoaded(context);
}

1.初始化SpringApplication 從spring.factories 讀取 listener ApplicationContextInitializer 。

2.運(yùn)行run方法

3.讀取 環(huán)境變量 配置信息…

4.創(chuàng)建springApplication上下文:ServletWebServerApplicationContext

5.預(yù)初始化上下文 : 讀取啟動(dòng)類

6.調(diào)用refresh 加載ioc容器

7.加載所有的自動(dòng)配置類

8.創(chuàng)建servlet容器

9.ps.在這個(gè)過程中springboot會(huì)調(diào)用很多監(jiān)聽器對(duì)外進(jìn)行擴(kuò)展

看一下內(nèi)置tomcat如何啟動(dòng)的:

Springboot的spring容器ServletWebServerApplicationContext對(duì)象重新了refresh()方法中的onRefresh()放用來(lái)啟動(dòng)tomcat

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
//創(chuàng)建tomcat
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

createWebServer創(chuàng)建tomcat的方法(也可以是Jetty,根據(jù)配置)

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
//如果servletContext 為null說明是內(nèi)置tomcat,不為null,則使用的是外置tomcat,這個(gè)servletContext 是tomcat創(chuàng)建傳入的;
   if (webServer == null && servletContext == null) {
      StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
//獲取servlet容器創(chuàng)建工廠,tomcat的是TomcatServletWebServerFactory
      ServletWebServerFactory factory = getWebServerFactory();
      createWebServer.tag("factory", factory.getClass().toString());
//創(chuàng)建內(nèi)置tomcat對(duì)象,并啟動(dòng);getSelfInitializer()這個(gè)方法也是很重要的,返回的是ServletWebServerApplicationContext#selfInitialize方法的引用函數(shù),其作用是tomcat啟動(dòng)時(shí)會(huì)回調(diào)此方法,并傳入servletContext對(duì)象,進(jìn)行DispacherServlet添加到servletContext中,把當(dāng)前spring容器和filter過濾器也添加到servletContext中
      this.webServer = factory.getWebServer(getSelfInitializer());
      createWebServer.end();
      getBeanFactory().registerSingleton("webServerGracefulShutdown",
            new WebServerGracefulShutdownLifecycle(this.webServer));
      getBeanFactory().registerSingleton("webServerStartStop",
            new WebServerStartStopLifecycle(this, this.webServer));
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context", ex);
      }
   }
   initPropertySources();
}

selfInitializ方法

private void selfInitialize(ServletContext servletContext) throws ServletException {
//把spring容器和servletContext進(jìn)行相互引用
   prepareWebApplicationContext(servletContext);
   registerApplicationScope(servletContext);
   WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//servletContext中添加Dispacherservlet和多個(gè)fileter
   for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
      beans.onStartup(servletContext);
   }
}

到此這篇關(guān)于Springboot啟動(dòng)原理詳細(xì)講解的文章就介紹到這了,更多相關(guān)Springboot啟動(dòng)原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Spring容器的使用流程

    詳解Spring容器的使用流程

    今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Spring容器的使用流程展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • mybatis plus or and 的合并寫法實(shí)例

    mybatis plus or and 的合并寫法實(shí)例

    這篇文章主要介紹了mybatis plus or and 的合并寫法實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2021-02-02
  • mybatis數(shù)組和集合的長(zhǎng)度判斷及插入方式

    mybatis數(shù)組和集合的長(zhǎng)度判斷及插入方式

    這篇文章主要介紹了mybatis數(shù)組和集合的長(zhǎng)度判斷及插入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java設(shè)計(jì)模式之淺談模板方法模式

    Java設(shè)計(jì)模式之淺談模板方法模式

    今天給大家?guī)?lái)的是關(guān)于Java設(shè)計(jì)模式的相關(guān)知識(shí),文章圍繞著Java模板方法展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • 常用json與javabean互轉(zhuǎn)的方法實(shí)現(xiàn)

    常用json與javabean互轉(zhuǎn)的方法實(shí)現(xiàn)

    這篇文章主要介紹了常用json與javabean互轉(zhuǎn)的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • SpringCloud Open feign 使用okhttp 優(yōu)化詳解

    SpringCloud Open feign 使用okhttp 優(yōu)化詳解

    這篇文章主要介紹了SpringCloud Open feign 使用okhttp 優(yōu)化詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2021-02-02
  • Java并發(fā)編程之重入鎖與讀寫鎖

    Java并發(fā)編程之重入鎖與讀寫鎖

    這篇文章主要介紹了Java并發(fā)編程之重入鎖與讀寫鎖,文中相關(guān)實(shí)例代碼詳細(xì),測(cè)試可用,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • Java內(nèi)部類與匿名內(nèi)部類

    Java內(nèi)部類與匿名內(nèi)部類

    這篇文章主要介紹了Java內(nèi)部類與匿名內(nèi)部類,內(nèi)部類可以直接訪問外部類的成員,包括私有成員。外部類要訪問內(nèi)部類的成員,必須要建立內(nèi)部類的對(duì)象,更多相關(guān)內(nèi)容可以參考下面文章內(nèi)容
    2022-06-06
  • Java使用云片API發(fā)送短信驗(yàn)證碼

    Java使用云片API發(fā)送短信驗(yàn)證碼

    這篇文章主要介紹了Java使用云片API發(fā)送短信驗(yàn)證碼,主要用的是Java實(shí)現(xiàn)短信驗(yàn)證碼。需要的朋友可以參考下
    2017-02-02
  • SpringAMQP的使用方式案例詳解

    SpringAMQP的使用方式案例詳解

    這篇文章主要介紹了SpringAMQP的使用方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01

最新評(píng)論