日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot启动流程是怎样的

發布時間:2024/4/13 javascript 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot启动流程是怎样的 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

SpringBoot一開始最讓我印象深刻的就是通過一個啟動類就能啟動應用。在SpringBoot以前,啟動應用雖然也不麻煩,但是還是有點繁瑣,要打包成war包,又要配置tomcat,tomcat又有一個server.xml文件去配置。

然而SpringBoot則內置了tomcat,通過啟動類啟動,配置也集中在一個application.yml中,簡直不要太舒服。

一、啟動類

首先我們看最常見的啟動類寫法。

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

把啟動類分解一下,實際上就是兩部分:

  • @SpringBootApplication注解
  • 一個main()方法,里面調用SpringApplication.run()方法。

二、@SpringBootApplication

首先看@SpringBootApplication注解的源碼。

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {}

很明顯,@SpringBootApplication注解由三個注解組合而成,分別是:

  • @ComponentScan
  • @EnableAutoConfiguration
  • @SpringBootConfiguration

@ComponentScan

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan {}

這個注解的作用是告訴Spring掃描哪個包下面類,加載符合條件的組件(比如貼有@Component和@Repository等的類)或者bean的定義。

所以有一個basePackages的屬性,如果默認不寫,則從聲明@ComponentScan所在類的package進行掃描。

所以啟動類最好定義在Root package下,因為一般我們在使用@SpringBootApplication時,都不指定basePackages的。

@EnableAutoConfiguration

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}

這是一個復合注解,看起來很多注解,實際上關鍵在@Import注解,它會加載AutoConfigurationImportSelector類,然后就會觸發這個類的selectImports()方法。根據返回的String數組(配置類的Class的名稱)加載配置類。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {//返回的String[]數組,是配置類Class的類名@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);//返回配置類的類名return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());} }

我們一直點下去,就可以找到最后的幕后英雄,就是SpringFactoriesLoader類,通過loadSpringFactories()方法加載META-INF/spring.factories中的配置類。

這里使用了spring.factories文件的方式加載配置類,提供了很好的擴展性。

所以@EnableAutoConfiguration注解的作用其實就是開啟自動配置,自動配置主要則依靠這種加載方式來實現。

@SpringBootConfiguration

@SpringBootConfiguration繼承自@Configuration,二者功能也一致,標注當前類是配置類, 并會將當前類內聲明的一個或多個以@Bean注解標記的方法的實例納入到spring容器中,并且實例名就是方法名。

小結

我們在這里畫張圖把@SpringBootApplication注解包含的三個注解分別解釋一下。

SpringApplication類

接下來講main方法里執行的這句代碼,這是SpringApplication類的靜態方法run()。

//啟動類的main方法 public static void main(String[] args) {SpringApplication.run(SpringmvcApplication.class, args); }//啟動類調的run方法 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {//調的是下面的,參數是數組的run方法return run(new Class<?>[] { primarySource }, args); }//和上面的方法區別在于第一個參數是一個數組 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {//實際上new一個SpringApplication實例,調的是一個實例方法run()return new SpringApplication(primarySources).run(args); }

通過上面的源碼,發現實際上最后調的并不是靜態方法,而是實例方法,需要new一個SpringApplication實例,這個構造器還帶有一個primarySources的參數。所以我們直接定位到構造器。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;//斷言primarySources不能為null,如果為null,拋出異常提示Assert.notNull(primarySources, "PrimarySources must not be null");//啟動類傳入的Classthis.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//判斷當前項目類型,有三種:NONE、SERVLET、REACTIVEthis.webApplicationType = WebApplicationType.deduceFromClasspath();//設置ApplicationContextInitializersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//設置監聽器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//判斷主類,初始化入口類this.mainApplicationClass = deduceMainApplicationClass(); }//判斷主類 private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null; }

得到SpringApplication實例后,接下來就調用實例方法run()。繼續看。

public ConfigurableApplicationContext run(String... args) {//創建計時器StopWatch stopWatch = new StopWatch();//開始計時stopWatch.start();//定義上下文對象ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();//Headless模式設置configureHeadlessProperty();//加載SpringApplicationRunListeners監聽器SpringApplicationRunListeners listeners = getRunListeners(args);//發送ApplicationStartingEvent事件listeners.starting();try {//封裝ApplicationArguments對象ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//配置環境模塊ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//根據環境信息配置要忽略的bean信息configureIgnoreBeanInfo(environment);//打印Banner標志Banner printedBanner = printBanner(environment);//創建ApplicationContext應用上下文context = createApplicationContext();//加載SpringBootExceptionReporterexceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//ApplicationContext基本屬性配置prepareContext(context, environment, listeners, applicationArguments, printedBanner);//刷新上下文refreshContext(context);//刷新后的操作,由子類去擴展afterRefresh(context, applicationArguments);//計時結束stopWatch.stop();//打印日志if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//發送ApplicationStartedEvent事件,標志spring容器已經刷新,此時所有的bean實例都已經加載完畢listeners.started(context);//查找容器中注冊有CommandLineRunner或者ApplicationRunner的bean,遍歷并執行run方法callRunners(context, applicationArguments);}catch (Throwable ex) {//發送ApplicationFailedEvent事件,標志SpringBoot啟動失敗handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {//發送ApplicationReadyEvent事件,標志SpringApplication已經正在運行,即已經成功啟動,可以接收服務請求。listeners.running(context);}catch (Throwable ex) {//報告異常,但是不發送任何事件handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context; }

總結

表面啟動類看起來就一個@SpringBootApplication注解,一個run()方法。其實是經過高度封裝后的結果。我們可以從這個分析中學到很多東西。比如使用了spring.factories文件來完成自動配置,提高了擴展性。在啟動時使用觀察者模式,以事件發布的形式通知,降低耦合,易于擴展等等。

?

總結

以上是生活随笔為你收集整理的SpringBoot启动流程是怎样的的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。