摘要:眾所周知,類上面帶有注解的類,即為的啟動(dòng)類。一個(gè)項(xiàng)目只能有一個(gè)啟動(dòng)類。根據(jù)是否是環(huán)境創(chuàng)建默認(rèn)的,通過(guò)掃描所有注解類來(lái)加載和最后通過(guò)實(shí)例化上下文對(duì)象,并返回。
??眾所周知,類上面帶有@SpringBootApplication注解的類,即為springboot的啟動(dòng)類。一個(gè)springboot項(xiàng)目只能有一個(gè)啟動(dòng)類。我們來(lái)分析一下SpringBoot項(xiàng)目的啟動(dòng)過(guò)程,首先看看啟動(dòng)類里面都包含什么
@SpringBootApplication public class HelloWorldMainApplication { public static void main(String[] args) { //spring應(yīng)用啟動(dòng)起來(lái) SpringApplication.run(HelloWorldMainApplication.class,args); } }
??從上面的代碼中可以看出真正起作用的是SpringApplication.run();這個(gè)方法,下面主要分析一下這個(gè)方法。
??SpringApplication初始化時(shí)主要做三件事情:
1.根據(jù)classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啟動(dòng)一個(gè)web applicationContext
2.SpringFactoriesInstances加載classpath下所有可用的ApplicationContextInitializer
3.SpringFactoriesInstances加載classpath下所有可用的ApplicationListener
/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified primary sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param resourceLoader the resource loader to use * @param primarySources the primary bean sources * @see #run(Class, String[]) * @see #setSources(Set) */ @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //1.根據(jù)classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啟動(dòng)一個(gè)web applicationContext this.webApplicationType = WebApplicationType.deduceFromClasspath(); //2.SpringFactoriesInstances加載classpath下所有可用的ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //3.SpringFactoriesInstances加載classpath下所有可用的ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }二、實(shí)例化完成后調(diào)用run()方法
??調(diào)用run()方法執(zhí)行的過(guò)程主要分為以下幾步:
1.遍歷SpringApplication初始化過(guò)程中加載的SpringApplicationRunListeners
2.調(diào)用Starting()監(jiān)聽(tīng)SpringApplication的啟動(dòng)
3.加載SpringBoot配置環(huán)境(ConfigurableEnvironment)
4.設(shè)置banner屬性
5.創(chuàng)建ConfigurableApplicationContext(應(yīng)用配置上下文)
6.將listeners、environment、applicationArguments、bannner等重要組件與上下文對(duì)象關(guān)聯(lián)
7.bean的實(shí)力化完成
/** * 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) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection1.遍歷SpringApplication初始化過(guò)程中加載的SpringApplicationRunListenersexceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //1.遍歷SpringApplication初始化過(guò)程中加載的SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); //2.調(diào)用starting()監(jiān)聽(tīng)SpringApplication的啟動(dòng) listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //3.加載SpringBoot配置環(huán)境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //4.設(shè)置banner屬性 Banner printedBanner = printBanner(environment); //5.創(chuàng)建ConfigurableApplicationContext(應(yīng)用配置上下文) context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //6.將listeners、environment、applicationArguments、banner等重要組件與上下文對(duì)象關(guān)聯(lián) prepareContext(context, environment, listeners, applicationArguments, printedBanner); //7.實(shí)例化bean refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
private SpringApplicationRunListeners getRunListeners(String[] args) { Class>[] types = new Class>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }2.調(diào)用Starting()監(jiān)聽(tīng)SpringApplication的啟動(dòng)
public void starting() { //遍歷所有的SpringApplicationRunListener,調(diào)用starting()方法監(jiān)聽(tīng)SpringApplication的啟動(dòng) for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }3.加載SpringBoot配置環(huán)境(ConfigurableEnvironment)
??加載SpringBoot配置環(huán)境(configurableEnvironment),如果是通過(guò)web容器發(fā)布,會(huì)加載StandardEnvironment。將配置文件(Environment)加入到監(jiān)聽(tīng)器對(duì)象中(SpringApplicationRunListeners)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment //如果environment不為空直接返回 || 如果是web環(huán)境則直接實(shí)例化StandardServletEnvironment類 || 如果不是web環(huán)境則直接實(shí)例化StandardEnvironment類 ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置環(huán)境信息 configureEnvironment(environment, applicationArguments.getSourceArgs()); //通知所有的監(jiān)聽(tīng)者,環(huán)境已經(jīng)準(zhǔn)備好了 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }4.設(shè)置banner屬性
private Banner printBanner(ConfigurableEnvironment environment) { //如果未開(kāi)啟banner打印直接返回 if (this.bannerMode == Banner.Mode.OFF) { return null; } //創(chuàng)建ResourceLoader對(duì)象 ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); //創(chuàng)建SpringApplicationBannerPrinter,該對(duì)象用來(lái)打印banner SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner); //如果bannerMode模式為L(zhǎng)OG,則將bannner打印到log文件中 if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } //打印banner到控制臺(tái) return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }5.初始化ConfigurableApplicationContext(應(yīng)用配置上下文)
??在SpringBoot中,應(yīng)用類型分為三類
public enum WebApplicationType { /** * The application should not run as a web application and should not start an * embedded web server. */ // 應(yīng)用程序不是web應(yīng)用,也不應(yīng)該用web服務(wù)器去啟動(dòng) NONE, /** * The application should run as a servlet-based web application and should start an * embedded servlet web server. */ //應(yīng)用程序應(yīng)作為基于servlet的web應(yīng)用程序運(yùn)行,并應(yīng)啟動(dòng)嵌入式servlet web(tomcat)服務(wù)器 SERVLET, /** * The application should run as a reactive web application and should start an * embedded reactive web server. */ //應(yīng)用程序應(yīng)作為 reactive web應(yīng)用程序運(yùn)行,并應(yīng)啟動(dòng)嵌入式 reactive web服務(wù)器。 REACTIVE; }
??根據(jù)webEnvironment是否是web環(huán)境創(chuàng)建默認(rèn)的contextClass,AnnotationConfigEnbeddedWebApplicationContext(通過(guò)掃描所有注解類來(lái)加載bean)和ConfigurableWebApplicationContext),最后通過(guò)BeanUtils實(shí)例化上下文對(duì)象,并返回。
/** * Strategy method used to create the {@link ApplicationContext}. By default this * method will respect any explicitly set application context or application context * class before falling back to a suitable default. * @return the application context (not yet refreshed) * @see #setApplicationContextClass(Class) */ protected ConfigurableApplicationContext createApplicationContext() { //根據(jù)webEnvironment是否是web環(huán)境創(chuàng)建默認(rèn)的contextClass Class> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: //AnnotationConfigServletWebServerApplicationContext contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: //AnnotationConfigReactiveWebServerApplicationContext contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: //AnnotationConfigApplicationContext contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } //BeanUtils實(shí)例化上下文對(duì)象 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }6.將listeners、environment、applicationArguments、banner等重要組件與上下文對(duì)象關(guān)聯(lián)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //設(shè)置上下文的environment context.setEnvironment(environment); //應(yīng)用上下文后處理 postProcessApplicationContext(context); //在context refresh之前,對(duì)其應(yīng)用ApplicationContextInitializer applyInitializers(context); //上下文準(zhǔn)備 listeners.contextPrepared(context); //打印啟動(dòng)日志和啟動(dòng)應(yīng)用的profile if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //向beanFactory注冊(cè)單例bean:命令行參數(shù)bean beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { //向beanFactory注冊(cè)單例bean:banner bean beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources //獲取SpringApplication的primarySources屬性 Set7.bean的實(shí)例化完成,刷新應(yīng)用上下文
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/75999.html
摘要:實(shí)例定義一個(gè)實(shí)現(xiàn),并納入到容器中進(jìn)行處理啟動(dòng)定義一個(gè)實(shí)現(xiàn),并納入到容器處理應(yīng)用已經(jīng)成功啟動(dòng)啟動(dòng)類測(cè)試,也可以直接在容器訪問(wèn)該值,配置參數(shù),然后執(zhí)行啟動(dòng)類打印結(jié)果接口發(fā)現(xiàn)二者的官方一樣,區(qū)別在于接收的參數(shù)不一樣。引言 我們?cè)谑褂肧pringBoot搭建項(xiàng)目的時(shí)候,如果希望在項(xiàng)目啟動(dòng)完成之前,能夠初始化一些操作,針對(duì)這種需求,可以考慮實(shí)現(xiàn)如下兩個(gè)接口(任一個(gè)都可以) org.springfram...
摘要:中有兩個(gè)接口能實(shí)現(xiàn)該功能和。首先了解一下的基本用法,可以在系統(tǒng)啟動(dòng)后執(zhí)行里面的方法執(zhí)行數(shù)據(jù)初始化如果有多個(gè)類的話也可以通過(guò)注解指定每個(gè)類的執(zhí)行順序。 (一)概述 最...
摘要:這里有一個(gè)參數(shù),主要是用來(lái)指定該配置項(xiàng)在配置文件中的前綴。創(chuàng)建一個(gè)配置類,里面沒(méi)有顯式聲明任何的,然后將剛才創(chuàng)建的導(dǎo)入。創(chuàng)建實(shí)現(xiàn)類,返回的全類名。創(chuàng)建實(shí)現(xiàn)類,實(shí)現(xiàn)方法直接手動(dòng)注冊(cè)一個(gè)名叫的到容器中。前言 小伙伴們是否想起曾經(jīng)被 SSM 整合支配的恐懼?相信很多小伙伴都是有過(guò)這樣的經(jīng)歷的,一大堆配置問(wèn)題,各種排除掃描,導(dǎo)入一個(gè)新的依賴又得添加新的配置。自從有了 SpringBoot 之后,咋...
摘要:本章目標(biāo)修改啟動(dòng)內(nèi)容構(gòu)建項(xiàng)目本章不涉及業(yè)務(wù)邏輯相關(guān)內(nèi)容,簡(jiǎn)單創(chuàng)建一個(gè)框架即可。的隱藏隱藏的方式提供了兩種,不過(guò)其中方式已經(jīng)被拋棄掉了,我們下面介紹下修改配置的方式。 Banner是SpringBoot框架一個(gè)特色的部分,其設(shè)計(jì)的目的無(wú)非就是一個(gè)框架的標(biāo)識(shí),其中包含了版本號(hào)、框架名稱等內(nèi)容,既然SpringBoot為我們提供了這個(gè)模塊,它肯定也是可以更換的這也是Spring開(kāi)源框架的設(shè)計(jì)...
閱讀 3525·2023-04-26 00:16
閱讀 1361·2021-11-25 09:43
閱讀 3824·2021-11-23 09:51
閱讀 2964·2021-09-24 09:55
閱讀 713·2021-09-22 15:45
閱讀 1387·2021-07-30 15:30
閱讀 3064·2019-08-30 14:04
閱讀 2237·2019-08-26 13:46