<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          springboot 源碼理解

          共 6849字,需瀏覽 14分鐘

           ·

          2020-12-24 00:48

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達(dá)

          66套java從入門到精通實(shí)戰(zhàn)課程分享

          最近在看springboot 啟動流程,以下就是我自己對springboot啟動時執(zhí)行流程的一些理解。我使用的springboot版本為 2.2.11.

          開始源碼閱讀

          package?com.example.demo;
          ?
          import?org.springframework.boot.SpringApplication;
          import?org.springframework.boot.autoconfigure.SpringBootApplication;
          ?
          @SpringBootApplication
          public?class?DemoApplication?{
          ?
          ?public?static?void?main(String[]?args)?{
          ?
          ??SpringApplication.run(DemoApplication.class,args);
          ?}
          }

          ????/**
          ??*?Static?helper?that?can?be?used?to?run?a?{@link?SpringApplication}?from?the
          ??*?specified?sources?using?default?settings?and?user?supplied?arguments.
          ??*?@param?primarySources?the?primary?sources?to?load
          ??*?@param?args?the?application?arguments?(usually?passed?from?a?Java?main?method)
          ??*?@return?the?running?{@link?ApplicationContext}
          ??*/
          ?public?static?ConfigurableApplicationContext?run(Class[]?primarySources,?String[]?args)?{
          ??return?new?SpringApplication(primarySources).run(args);
          ?}

          由此可以看出springboot 在啟動的過程主要是通過兩個部分來進(jìn)行完成的。1,構(gòu)造器初始化 2.run方法執(zhí)行的邏輯。? ? ?

          首先看下構(gòu)造器里面實(shí)現(xiàn)的邏輯,我先上一張圖,主要的執(zhí)行如下圖所示。

          ?

          public?SpringApplication(ResourceLoader?resourceLoader,?Class...?primarySources)?{
          ??
          ????????//?默認(rèn)為null
          ????????this.resourceLoader?=?resourceLoader;
          ??Assert.notNull(primarySources,?"PrimarySources?must?not?be?null");
          ????????//?1.?初始化主的資源
          ??this.primarySources?=?new?LinkedHashSet<>(Arrays.asList(primarySources));
          ????????//?2.?推斷應(yīng)用類型?
          ??this.webApplicationType?=?WebApplicationType.deduceFromClasspath();
          ????????//?3.?初始化所有實(shí)現(xiàn)?ApplicationContextInitializer的子類
          ??setInitializers((Collection)?getSpringFactoriesInstances(ApplicationContextInitializer.class));
          ????????//?4.?初始化所有的實(shí)現(xiàn)ApplicationListener?的監(jiān)聽器,過程和3中的邏輯是一樣的
          ??setListeners((Collection)?getSpringFactoriesInstances(ApplicationListener.class));
          ????????//?5.?獲取mian方法??(這塊的思路比較好,以下會有介紹)
          ??this.mainApplicationClass?=?deduceMainApplicationClass();
          ?}

          ?2.推斷應(yīng)用類型,這個就是根據(jù)類是否加載來判斷應(yīng)用的類型,我引用的包為spring-boot-starter-web 因此為SERVLET類型,感興趣的可以看下這塊源碼。

          ?3. 初始化所有實(shí)現(xiàn)ApplicationContextInitializer類型應(yīng)用上下文

          private??Collection?getSpringFactoriesInstances(Class?type)?{
          ??return?getSpringFactoriesInstances(type,?new?Class[]?{});
          ?}
          ?
          private??Collection?getSpringFactoriesInstances(Class?type,?Class[]?parameterTypes,?Object...?args)?{
          ????????//?獲取類加載器
          ??ClassLoader?classLoader?=?getClassLoader();
          ??//?Use?names?and?ensure?unique?to?protect?against?duplicates
          ????????//?獲取工程名稱
          ??Set?names?=?new?LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type,?classLoader));
          ????????//?創(chuàng)建bena工廠實(shí)例
          ??List?instances?=?createSpringFactoriesInstances(type,?parameterTypes,?classLoader,?args,?names);
          ????????//?排序
          ??AnnotationAwareOrderComparator.sort(instances);
          ??return?instances;
          ?}

          其中SpringFactoriesLoader.loadFactoryNames(type, classLoader) 這個方式是重點(diǎn),意思就是在classpath 路徑下獲取接口的實(shí)現(xiàn)類名稱,獲取之后將其放入到緩存中,方便后續(xù)的處理

          public?static?List?loadFactoryNames(Class?factoryType,?@Nullable?ClassLoader?classLoader)?{
          ??String?factoryTypeName?=?factoryType.getName();
          ??return?loadSpringFactories(classLoader).getOrDefault(factoryTypeName,?Collections.emptyList());
          ?}
          ?
          ?private?static?Map>?loadSpringFactories(@Nullable?ClassLoader?classLoader)?{
          ??MultiValueMap?result?=?cache.get(classLoader);
          ??if?(result?!=?null)?{
          ???return?result;
          ??}
          ?
          ??try?{
          ????????//?FACTORIES_RESOURCE_LOCATION?=?"META-INF/spring.factories";
          ????????//?獲取加載路徑
          ???Enumeration?urls?=?(classLoader?!=?null??
          ?????classLoader.getResources(FACTORIES_RESOURCE_LOCATION)?:
          ?????ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
          ???result?=?new?LinkedMultiValueMap<>();
          ???while?(urls.hasMoreElements())?{
          ????URL?url?=?urls.nextElement();
          ????UrlResource?resource?=?new?UrlResource(url);
          ????Properties?properties?=?PropertiesLoaderUtils.loadProperties(resource);
          ????for?(Map.Entry?entry?:?properties.entrySet())?{
          ?????String?factoryTypeName?=?((String)?entry.getKey()).trim();
          ?????for?(String?factoryImplementationName?:?StringUtils.commaDelimitedListToStringArray((String)?entry.getValue()))?{
          ??????result.add(factoryTypeName,?factoryImplementationName.trim());
          ?????}
          ????}
          ???}
          ???cache.put(classLoader,?result);
          ???return?result;
          ??}
          ??catch?(IOException?ex)?{
          ???throw?new?IllegalArgumentException("Unable?to?load?factories?from?location?["?+
          ?????FACTORIES_RESOURCE_LOCATION?+?"]",?ex);
          ??}
          ?}

          4. 中獲取監(jiān)聽器的方法和3中的是相同的套路。

          5. 中獲取main方法的思路比較好,是直接new出一個運(yùn)行期的異常通過堆棧信息來獲取main方法,這個思路比較好。

          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;
          ?}

          到此springboot實(shí)例化中做的初始化工作已經(jīng)完成了,接下來就是run方法中的邏輯了。

          public?ConfigurableApplicationContext?run(String...?args)?{
          ????????//?計(jì)時器,相當(dāng)于System.currentTimeMillis()獲取時間進(jìn)行相減來獲取方法的耗時時間
          ??StopWatch?stopWatch?=?new?StopWatch();
          ??stopWatch.start();
          ??ConfigurableApplicationContext?context?=?null;
          ??Collection?exceptionReporters?=?new?ArrayList<>();
          ????????//?設(shè)置環(huán)境及沒有鼠標(biāo),鍵盤這樣的硬件信息
          ??configureHeadlessProperty();
          ????????//?獲取監(jiān)聽器
          ??SpringApplicationRunListeners?listeners?=?getRunListeners(args);
          ????????//?監(jiān)聽器開啟,進(jìn)行廣播時間
          ??listeners.starting();
          ??try?{
          ????????????//?獲取默認(rèn)的應(yīng)用參數(shù)
          ???ApplicationArguments?applicationArguments?=?new?DefaultApplicationArguments(args);
          ????????????//?加載系統(tǒng)配置及用戶自定義配置
          ???ConfigurableEnvironment?environment?=?prepareEnvironment(listeners,?applicationArguments);
          ????????????//?設(shè)置忽略的bean信息
          ???configureIgnoreBeanInfo(environment);
          ????????????//?打印banner,
          ???Banner?printedBanner?=?printBanner(environment);
          ????????????//?創(chuàng)建IOC容器
          ???context?=?createApplicationContext();
          ????????????//?加載異常的類型信息,和構(gòu)造方法中的是相同的套路
          ???exceptionReporters?=?getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          ?????new?Class[]?{?ConfigurableApplicationContext.class?},?context);
          ????????????//?IOC容器前置處理
          ???prepareContext(context,?environment,?listeners,?applicationArguments,?printedBanner);
          ????????????//?刷新容器
          ???refreshContext(context);
          ????????????//?IOC容器后置處理
          ???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;
          ?}

          總結(jié)了下run方法的主要執(zhí)行流程



          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/dangyongkang/article/details/111031858





          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ???

          ?長按上方微信二維碼?2 秒


          感謝點(diǎn)贊支持下哈?

          瀏覽 72
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91欧美亚洲 | 成年人A片 | 在线观看三区 | 人人干97 | 福利精品一区二区三区四区 |