摘要:眾所周知,類上面帶有注解的類,即為的啟動類。一個項目只能有一個啟動類。根據是否是環境創建默認的,通過掃描所有注解類來加載和最后通過實例化上下文對象,并返回。
??眾所周知,類上面帶有@SpringBootApplication注解的類,即為springboot的啟動類。一個springboot項目只能有一個啟動類。我們來分析一下SpringBoot項目的啟動過程,首先看看啟動類里面都包含什么
@SpringBootApplication public class HelloWorldMainApplication { public static void main(String[] args) { //spring應用啟動起來 SpringApplication.run(HelloWorldMainApplication.class,args); } }
??從上面的代碼中可以看出真正起作用的是SpringApplication.run();這個方法,下面主要分析一下這個方法。
??SpringApplication初始化時主要做三件事情:
1.根據classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啟動一個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.根據classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啟動一個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(); }二、實例化完成后調用run()方法
??調用run()方法執行的過程主要分為以下幾步:
1.遍歷SpringApplication初始化過程中加載的SpringApplicationRunListeners
2.調用Starting()監聽SpringApplication的啟動
3.加載SpringBoot配置環境(ConfigurableEnvironment)
4.設置banner屬性
5.創建ConfigurableApplicationContext(應用配置上下文)
6.將listeners、environment、applicationArguments、bannner等重要組件與上下文對象關聯
7.bean的實力化完成
/** * 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初始化過程中加載的SpringApplicationRunListenersexceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //1.遍歷SpringApplication初始化過程中加載的SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); //2.調用starting()監聽SpringApplication的啟動 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //3.加載SpringBoot配置環境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //4.設置banner屬性 Banner printedBanner = printBanner(environment); //5.創建ConfigurableApplicationContext(應用配置上下文) context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //6.將listeners、environment、applicationArguments、banner等重要組件與上下文對象關聯 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //7.實例化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.調用Starting()監聽SpringApplication的啟動
public void starting() { //遍歷所有的SpringApplicationRunListener,調用starting()方法監聽SpringApplication的啟動 for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }3.加載SpringBoot配置環境(ConfigurableEnvironment)
??加載SpringBoot配置環境(configurableEnvironment),如果是通過web容器發布,會加載StandardEnvironment。將配置文件(Environment)加入到監聽器對象中(SpringApplicationRunListeners)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment //如果environment不為空直接返回 || 如果是web環境則直接實例化StandardServletEnvironment類 || 如果不是web環境則直接實例化StandardEnvironment類 ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置環境信息 configureEnvironment(environment, applicationArguments.getSourceArgs()); //通知所有的監聽者,環境已經準備好了 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }4.設置banner屬性
private Banner printBanner(ConfigurableEnvironment environment) { //如果未開啟banner打印直接返回 if (this.bannerMode == Banner.Mode.OFF) { return null; } //創建ResourceLoader對象 ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); //創建SpringApplicationBannerPrinter,該對象用來打印banner SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner); //如果bannerMode模式為LOG,則將bannner打印到log文件中 if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } //打印banner到控制臺 return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }5.初始化ConfigurableApplicationContext(應用配置上下文)
??在SpringBoot中,應用類型分為三類
public enum WebApplicationType { /** * The application should not run as a web application and should not start an * embedded web server. */ // 應用程序不是web應用,也不應該用web服務器去啟動 NONE, /** * The application should run as a servlet-based web application and should start an * embedded servlet web server. */ //應用程序應作為基于servlet的web應用程序運行,并應啟動嵌入式servlet web(tomcat)服務器 SERVLET, /** * The application should run as a reactive web application and should start an * embedded reactive web server. */ //應用程序應作為 reactive web應用程序運行,并應啟動嵌入式 reactive web服務器。 REACTIVE; }
??根據webEnvironment是否是web環境創建默認的contextClass,AnnotationConfigEnbeddedWebApplicationContext(通過掃描所有注解類來加載bean)和ConfigurableWebApplicationContext),最后通過BeanUtils實例化上下文對象,并返回。
/** * 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() { //根據webEnvironment是否是web環境創建默認的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實例化上下文對象 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }6.將listeners、environment、applicationArguments、banner等重要組件與上下文對象關聯
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //設置上下文的environment context.setEnvironment(environment); //應用上下文后處理 postProcessApplicationContext(context); //在context refresh之前,對其應用ApplicationContextInitializer applyInitializers(context); //上下文準備 listeners.contextPrepared(context); //打印啟動日志和啟動應用的profile if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //向beanFactory注冊單例bean:命令行參數bean beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { //向beanFactory注冊單例bean:banner bean beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources //獲取SpringApplication的primarySources屬性 Set7.bean的實例化完成,刷新應用上下文
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/75999.html
摘要:實例定義一個實現,并納入到容器中進行處理啟動定義一個實現,并納入到容器處理應用已經成功啟動啟動類測試,也可以直接在容器訪問該值,配置參數,然后執行啟動類打印結果接口發現二者的官方一樣,區別在于接收的參數不一樣。引言 我們在使用SpringBoot搭建項目的時候,如果希望在項目啟動完成之前,能夠初始化一些操作,針對這種需求,可以考慮實現如下兩個接口(任一個都可以) org.springfram...
摘要:中有兩個接口能實現該功能和。首先了解一下的基本用法,可以在系統啟動后執行里面的方法執行數據初始化如果有多個類的話也可以通過注解指定每個類的執行順序。 (一)概述 最...
摘要:這里有一個參數,主要是用來指定該配置項在配置文件中的前綴。創建一個配置類,里面沒有顯式聲明任何的,然后將剛才創建的導入。創建實現類,返回的全類名。創建實現類,實現方法直接手動注冊一個名叫的到容器中。前言 小伙伴們是否想起曾經被 SSM 整合支配的恐懼?相信很多小伙伴都是有過這樣的經歷的,一大堆配置問題,各種排除掃描,導入一個新的依賴又得添加新的配置。自從有了 SpringBoot 之后,咋...
摘要:本章目標修改啟動內容構建項目本章不涉及業務邏輯相關內容,簡單創建一個框架即可。的隱藏隱藏的方式提供了兩種,不過其中方式已經被拋棄掉了,我們下面介紹下修改配置的方式。 Banner是SpringBoot框架一個特色的部分,其設計的目的無非就是一個框架的標識,其中包含了版本號、框架名稱等內容,既然SpringBoot為我們提供了這個模塊,它肯定也是可以更換的這也是Spring開源框架的設計...
閱讀 3553·2023-04-26 00:16
閱讀 1368·2021-11-25 09:43
閱讀 3837·2021-11-23 09:51
閱讀 2975·2021-09-24 09:55
閱讀 726·2021-09-22 15:45
閱讀 1403·2021-07-30 15:30
閱讀 3074·2019-08-30 14:04
閱讀 2255·2019-08-26 13:46