一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Spring Boot啟動(dòng)流程分析

Spring Boot啟動(dòng)流程分析

2021-01-08 11:37lance的java小菜 Java教程

本文給大家介紹spring boot是怎樣啟動(dòng)和啟動(dòng)做了哪些事情。具體內(nèi)容詳情大家通過本文詳細(xì)學(xué)習(xí)吧

引言

早在15年的時(shí)候就開始用spring boot進(jìn)行開發(fā)了,然而一直就只是用用,并沒有深入去了解spring boot是以什么原理怎樣工作的,說來也慚愧。今天讓我們從spring boot啟動(dòng)開始,深入了解一下spring boot的工作原理。

為什么用spring boot

在使用一個(gè)東西或者一個(gè)工具之前,我們總是會(huì)問自己,我為什么要用?用他能給我?guī)硎裁春锰帲?/p>

* 最大的好處就是spring boot遵從了java**約定大于配置**不用面對一大堆的配置文件,spring boot是根據(jù)你用的包來決定提供什么配置。

* 服務(wù)器以jar包的形式內(nèi)嵌于項(xiàng)目中,對于微服務(wù)滿天飛的情況,spring boot天生適合微服務(wù)架構(gòu),方便部署。

* 提供devtools從此改代碼就需重啟成為歷史。

有優(yōu)點(diǎn)就一定有缺點(diǎn),缺點(diǎn)來源于優(yōu)點(diǎn)優(yōu)點(diǎn)來源于缺點(diǎn)(感覺在說哲學(xué)問題了哈哈哈)

* 正因?yàn)榕渲脤﹂_發(fā)者不透明,不看源碼會(huì)不清楚spring boot如何進(jìn)行諸如JDBC加載、事務(wù)管理等,出現(xiàn)錯(cuò)誤也很難調(diào)錯(cuò)。

* 自動(dòng)配置之后要自定義配置需編碼javaConfig,需要了解這些配置類api。

* 版本迭代太快,新版本對老版本改動(dòng)太多導(dǎo)致不兼容,比如1.3.5之前的springBootTest和1.4.0之后的springBootTest。

只有合適的架構(gòu)才是最好的架構(gòu)如果能接受spring boot這些缺點(diǎn),spring boot確實(shí)是一個(gè)可以提高開發(fā)效率的不錯(cuò)的選擇。

啟動(dòng)流程

扯了這么多,該上正題了,讓我們來看看spring boot是怎樣啟動(dòng)和啟動(dòng)做了哪些事情。

以下代碼是spring boot項(xiàng)目標(biāo)準(zhǔn)的啟動(dòng)方式,使用注解@SpringBootApplication并且在main方法中調(diào)用SpringApplication的run方法,就可以完成。我們就從這個(gè)run方法開始看看spring boot的啟動(dòng)過程。

?
1
2
3
4
5
6
@SpringBootApplication
public class Application {
  public static void main(String[] args){
    SpringApplication.run(Application.class,args);
  }
}

我們進(jìn)入run方法,可以看到最終是調(diào)用了 new SpringApplication(sources).run(args);new SpringApplication(sources).run(args); 這個(gè)方法,可以看到,springBoot的啟動(dòng)可以分為兩個(gè)部分,第一部分:SpringApplication的實(shí)例化;第二部分:調(diào)用該實(shí)例運(yùn)行run方法。我們先來看看這個(gè)SpringApplication的實(shí)例化過程。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void initialize(Object[] sources) {
    if (sources != null && sources.length > 0) {
      this.sources.addAll(Arrays.asList(sources));
    }
    //判定是否為webEnvironment
    this.webEnvironment = deduceWebEnvironment();
    //實(shí)例化并加載所有可以加載的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    //實(shí)例化并加載所有可以加載的ApplicationListener
    setListeners((Collection)
    getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
  }

關(guān)鍵點(diǎn)在兩個(gè)set方法上**

setInitializers((Collection) getSpringFactoriesInstances(

ApplicationContextInitializer.class))** 和 **setListeners((Collection)

getSpringFactoriesInstances(ApplicationListener.class))** 這兩個(gè)方法一毛一樣,挑實(shí)例化ApplicationContextInitializer講一講。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
      //拿到類加載器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    //使用loadFactoryNames方法載入所有的ApplicationContextInitializer的類全限定名
    Set<String> names = new LinkedHashSet<String>(
        SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //使用反射將所有的ApplicationContextInitializer實(shí)例化  
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
        classLoader, args, names);
    //排序   
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
  }

自動(dòng)配置的關(guān)鍵就是這個(gè) getSpringFactoriesInstances方法,確切的說是這個(gè)方法里的loadFactoryNames方法,浪我們看看這個(gè)loadFactoryNames方法干了啥,咋就能實(shí)現(xiàn)自動(dòng)配置。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
      Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
      ArrayList result = new ArrayList();
      while(urls.hasMoreElements()) {
        URL url = (URL)urls.nextElement();
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
      }
      return result;
    } catch (IOException var8) {
      throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
    }
  }

可以看到這個(gè)方法就做了一件事,就是從META-INF/spring.factories這個(gè)路徑取出所有”url”來,我們可以去到這個(gè)路徑下看看到底是些啥?

?
1
2
3
4
5
6
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

這下大家都應(yīng)該明白了,spring是通過將所有你加載的jar包中找到它需要的ApplicationContextInitializer來進(jìn)行動(dòng)態(tài)的配置的,只要你有用到特定的maven包,初始化的時(shí)候會(huì)找這個(gè)包下的META-INF/spring.factories的需要的類比如ApplicationContextInitializer進(jìn)行實(shí)例化bean,你就可以用了,不需要任何配置。

說到這已經(jīng)將所有SpringApplication實(shí)例化說完了,只是在加載完ApplicationContextInitializer和ApplicationListener這之后還有一步,就是找到啟動(dòng)類所在的位置并且設(shè)入屬性mainApplicationClass中。

接下來讓我們回到new SpringApplication(sources).run(args)方法來看看run方法是怎么run的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public ConfigurableApplicationContext run(String... args) {
    //開啟啟動(dòng)計(jì)時(shí)器,項(xiàng)目啟動(dòng)完會(huì)打印執(zhí)行時(shí)間出來
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    configureHeadlessProperty();
    //獲取SpringApplicationRunListener并啟動(dòng)監(jiān)聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
          args);
      //環(huán)境變量的加載   
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
          applicationArguments);
      //啟動(dòng)后console的打印出來的一堆配置信息   
      Banner printedBanner = printBanner(environment);
      //終極大boss->ApplicationContext實(shí)例化
      context = createApplicationContext();
      analyzers = new FailureAnalyzers(context);
      prepareContext(context, environment, listeners, applicationArguments,
          printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass)
            .logStarted(getApplicationLog(), stopWatch);
      }
      return context;
    }
    catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
    }
  }

從這個(gè)方法里面做的最關(guān)鍵的三件事情就是:

獲取監(jiān)聽器并啟動(dòng)

加載環(huán)境變量,該環(huán)境變量包括system environment、classpath environment和用戶自己加的application.properties

創(chuàng)建ApplicationContext

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    context.getBeanFactory().registerSingleton("springApplicationArguments",
        applicationArguments);
    if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    // Load the sources
    Set<Object> sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[sources.size()]));
    listeners.contextLoaded(context);
  }

前兩點(diǎn)沒什么好說的,重點(diǎn)說說第三個(gè),創(chuàng)建ApplicationContext。創(chuàng)建applicationContext又分為幾部:實(shí)例化applicationContext、prepareContext、refreshContext。實(shí)例化applicationContext會(huì)根據(jù)在之前我們說的webEnvironment這個(gè)屬性判斷是使用webContext類AnnotationConfigEmbeddedWebApplicationContext還是普通context類AnnotationConfigApplicationContext(在這里我們使用的是webContext為例)然后通過反射進(jìn)行實(shí)例化。applicationContext實(shí)例化完了會(huì)進(jìn)入prepareContext流程,這個(gè)prepareContext方法會(huì)加載之前準(zhǔn)備好的environment進(jìn)入context中,然后如果有beanNameGenerator和resourceLoader那么提前創(chuàng)建bean加載進(jìn)applicationContext,但是一般這兩個(gè)都是空的,所以直接進(jìn)入applyInitializers方法,將之前實(shí)例化的所有initializers進(jìn)行初始化,所有的bean就是在這里進(jìn)行bean的掃描和加載的因這次講的是啟動(dòng)過程,所以不再細(xì)講。最后把創(chuàng)建好的applicationContext設(shè)置進(jìn)入listener,prepareContext過程就結(jié)束了。最后是refreshContext,這個(gè)就和spring的bean加載過程一致了,bean的注入、beanFactory、postProcessBeanFactory等等,詳情可以去看看spring bean的生命周期。

總結(jié)

spring boot 初始化內(nèi)容還是很多的,但是總結(jié)起來就四點(diǎn):

* 創(chuàng)建SpringApplication實(shí)例,判定環(huán)境,是web環(huán)境還是普通環(huán)境。加載所有需要用到的Initializers和Listeners,這里使用約定大于配置的理念揭開了自動(dòng)配置的面紗。

* 加載環(huán)境變量,環(huán)境變量包括system environment、classpath environment、application environment(也就是我們自定義的application.properties配置文件)

* 創(chuàng)建SpringApplicationRunListeners

* 創(chuàng)建ApplicationContext,設(shè)置裝配context,在這里將所有的bean進(jìn)行掃描最后在refreshContext的時(shí)候進(jìn)行加載、注入。最終將裝配好的context作為屬性設(shè)置進(jìn)SpringApplicationRunListeners,這就完成了一個(gè)spring boot項(xiàng)目的啟動(dòng)。

以上所述是小編給大家介紹的Spring Boot啟動(dòng)流程,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!

原文鏈接:http://blog.csdn.net/nethackatschool/article/details/78051220

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩丝袜在线观看 | 乌克兰粉嫩摘花第一次 | 国产高清经典露脸3p | 特黄特色大片免费高清视频 | 媳妇和公公小说 | 免费港剧在线观看港剧 | 国产乱子伦真实china | 香蕉久久久久久狠狠色 | 99久热只有精品视频免费观看17 | 高清在线观看mv的网址免费 | 日韩亚洲国产欧美精品 | 日韩视频免费观看 | 日本孕妇与黑人xxxxxx | 91最新国产 | 日本www视频在线观看 | 日本最新伦中文字幕 | 车上小婕子系列辣文小说 | 欧洲一级黑寡妇 | 免费又爽又黄禁片视频在线播放 | 91美女在线视频 | 办公室操秘书 | 免费看片aⅴ免费大片 | 免费日批 | 8插8插| 缴情五月天 | 91在线视频导航 | 91看片淫黄大片欧美看国产片 | 亚洲老头与老太hd | 日韩每日更新 | 55夜色66夜亚州精品站 | 日韩一区二区三区四区五区 | 成人国产精品一区二区不卡 | 日韩欧美精品一区二区 | 亚洲免费色 | 456成人免费高清视频 | 兽皇日本 | 99热精品在线播放 | 亚洲 欧美 偷自乱 图片 | 小小水蜜桃免费影院 | 亚洲免费视频在线观看 | 从后面撕开老师的丝袜动态图 |