附3 springboot源碼解析 - 構建SpringApplication

来源:http://www.cnblogs.com/java-zhao/archive/2016/05/29/5540309.html
-Advertisement-
Play Games

SpringBoot啟動過程: 1、構建SpringApplication對象 2、執行run() 一、構建SpringApplication對象 說明: 實例化該類的時候會載入bean到applicationContext中去 這裡的入參是MySpringApplication.class這樣一個 ...


 1 package com.microservice.framework;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 
 6 @SpringBootApplication
 7 public class MySpringAplication {
 8 
 9     public void run(String[] args) {
10         SpringApplication sa = new SpringApplication(MySpringAplication.class);
11         sa.run(args);
12     }
13 
14 }

SpringBoot啟動過程:

1、構建SpringApplication對象

2、執行run()

一、構建SpringApplication對象

1     /**
2      * The application context will load beans from the specified sources 
3      */
4     public SpringApplication(Object... sources) {
5         initialize(sources);
6     }

說明:

  • 實例化該類的時候會載入bean到applicationContext中去
  • 這裡的入參是MySpringApplication.class這樣一個Class<com.microservice.framework.MySpringApplication>對象
    private final Set<Object> sources = new LinkedHashSet<Object>();
    private boolean webEnvironment;
    private Class<?> mainApplicationClass;

    private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
        this.webEnvironment = deduceWebEnvironment();
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

步驟:

  • 將傳入的MySpringApplication.class對象放入Set集合
  • 判斷是否是web環境
  • 創建ApplicationInitializer列表
  • 初始化ApplicationListener列表
  • 初始化主類mainApplicationClass

1.1、將傳入的MySpringApplication.class對象放入Set集合

1.2、判斷是否是web環境:

    private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" };

    private boolean deduceWebEnvironment() {
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return false;
            }
        }
        return true;
    }

說明:通過在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES這個數組中所包含的所有類(實際上就是2個類),如果存在那麼當前程式即是一個Web應用程式,反之則不然。 

1.3、創建ApplicationContextInitializer列表

 1     private List<ApplicationContextInitializer<?>> initializers;
 2 
 3     public void setInitializers(
 4             Collection<? extends ApplicationContextInitializer<?>> initializers) {
 5         this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
 6         this.initializers.addAll(initializers);
 7     }
 8 
 9     private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
10         return getSpringFactoriesInstances(type, new Class<?>[] {});
11     }
12 
13     private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
14             Class<?>[] parameterTypes, Object... args) {
15         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
16 
17         // Use names and ensure unique to protect against duplicates
18         Set<String> names = new LinkedHashSet<String>(
19                 SpringFactoriesLoader.loadFactoryNames(type, classLoader));
20         List<T> instances = new ArrayList<T>(names.size());
21 
22         // Create instances from the names
23         for (String name : names) {
24             try {
25                 Class<?> instanceClass = ClassUtils.forName(name, classLoader);
26                 Assert.isAssignable(type, instanceClass);
27                 Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);
28                 T instance = (T) constructor.newInstance(args);
29                 instances.add(instance);
30             }
31             catch (Throwable ex) {
32                 throw new IllegalArgumentException(
33                         "Cannot instantiate " + type + " : " + name, ex);
34             }
35         }
36 
37         AnnotationAwareOrderComparator.sort(instances);
38         return instances;
39     }

步驟:

  • 調用SpringFactoriesLoader.loadFactoryNames(type, classLoader)來獲取所有Spring Factories的名字,(這裡是獲取了四個ApplicationContextInitializer實現類的全類名,見下邊)
  • 為每一個Spring Factories根據讀取到的名字創建其對象。(這裡創建了4個對象)
  • 將創建好的對象列表排序並返回。

其中,SpringFactoriesLoader.loadFactoryNames(type, classLoader)如下:

 1     /**
 2      * The location to look for factories.
 3      * <p>Can be present in multiple JAR files.
 4      */
 5     public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
 6 
 7     /**
 8      * Load the fully qualified class names of factory implementations of the
 9      * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
10      * class loader.
11      */
12     public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
13         String factoryClassName = factoryClass.getName();
14         try {
15             Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
16                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
17             List<String> result = new ArrayList<String>();
18             while (urls.hasMoreElements()) {
19                 URL url = urls.nextElement();
20                 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
21                 String factoryClassNames = properties.getProperty(factoryClassName);
22                 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
23             }
24             return result;
25         }
26         catch (IOException ex) {
27             throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
28                     "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
29         }
30     }

META-INF/spring-factories

1 # Application Context Initializers
2 org.springframework.context.ApplicationContextInitializer=\
3 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
4 org.springframework.boot.context.ContextIdApplicationContextInitializer,\
5 org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
6 org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer

說明:

  • 所有jar獲取所有的META-INF/spring-factories文件。(這裡只有spring-boot-1.3.0.RELEASE.jar下有一個)
  • 遍歷每一個spring-factories文件,並獲取其下key為factoryClass.getName()(這裡是入參

    org.springframework.context.ApplicationContextInitializer)的value(這裡有以上四個ApplicationContextInitializer實現類)

以上四個類的作用:

 

至此,設置ApplicationContextInitialize就完成了。

總結:整個setInitializers實際上就是初始化了SpringApplication的屬性List<ApplicationContextInitializer<?>> initializers為一個ArrayList列表,該列表中有四個實例:

  • ConfigurationWarningsApplicationContextInitializer的實例
  • ContextIdApplicationContextInitializer的實例
  • DelegatingApplicationContextInitializer實例
  • ServerPortInfoApplicationContextInitializer實例

1.4、初始化ApplicationListener列表

 1     private List<ApplicationListener<?>> listeners;    
 2 
 3         /**
 4      * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
 5      * and registered with the {@link ApplicationContext}.
 6      * @param listeners the listeners to set
 7      */
 8     public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
 9         this.listeners = new ArrayList<ApplicationListener<?>>();
10         this.listeners.addAll(listeners);
11     }    

META-INF/spring-factories

 1 # Application Listeners
 2 org.springframework.context.ApplicationListener=\
 3 org.springframework.boot.builder.ParentContextCloserApplicationListener,\
 4 org.springframework.boot.context.FileEncodingApplicationListener,\
 5 org.springframework.boot.context.config.AnsiOutputApplicationListener,\
 6 org.springframework.boot.context.config.ConfigFileApplicationListener,\
 7 org.springframework.boot.context.config.DelegatingApplicationListener,\
 8 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
 9 org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
10 org.springframework.boot.logging.LoggingApplicationListener

以上八個listener的作用如下:

至此,整個setListeners方法結束,初始化了一個包含以上8個ApplicationListener實例的List集合。

 

1.5、初始化主類mainApplicationClass

 1     private Class<?> mainApplicationClass;
 2 
 3     private Class<?> deduceMainApplicationClass() {
 4         try {
 5             StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
 6             for (StackTraceElement stackTraceElement : stackTrace) {
 7                 if ("main".equals(stackTraceElement.getMethodName())) {
 8                     return Class.forName(stackTraceElement.getClassName());
 9                 }
10             }
11         }
12         catch (ClassNotFoundException ex) {
13             // Swallow and continue
14         }
15         return null;
16     }

說明:獲取main()方法所在的主類Class對象,並賦值給SpringApplication的mainApplicationClass屬性。

至此,SpringApplication對象初始化完成了。

總結:整個SpringApplication初始化的過程,就是初始化了

  • 一個包含入參MySpringApplication.class的sources的Set<Object>
  • 一個當前環境是否是web環境的boolean webEnvironment
  • 一個包含4個ApplicationContextInitializer實例的List
  • 一個包含8個ApplicationListener實例的List
  • 一個main方法所在的主類的Class對象。

註意:

本文基本參照http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow 完成,該文的作者已經解析的很好了,我這裡再抄一遍,只是為了加深記憶!!!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前面我們完成了微信公眾號的`伺服器配置`,並且完成了token的驗證.現在我們嘗試回覆用戶發送給公眾號的文本消息.1.首先我們確定`微信配置`為`啟用`狀態. 2.然後我們進入微信公眾平臺的`沙盒測試賬號`,因為在這種狀態下對已各種藉口的許可權限制比較小,而且這是一個獨立的公眾號,有獨立的`appID ...
  • 什麼是集合?正如其字面的意思,一堆東西集中合併到一起。乍一聽貌似和容器沒什麼差別,嗯,好吧,集合也算是一種容器。 在學習這個容器有什麼不同之前,先看看集合是如何創建的: 集合分為兩種,一種是不可變的,一種是可變的,兩者的差異後面會分析。 不過,我們創建了兩個空的集合貌似麽什麼意思。 為了使其有意義, ...
  • 摘要:python中有好多可用性特別強的內置函數,熟練掌握對於以後的編程過程中有很大的幫助~~~~ callable函數、chr函數與ord函數、random函數、compile函數、evec與eval函數、dir函數,divmod函數、isinstance函數、filter與map函數 ...
  • 搞開發的人,包括我自己,有時缺少了對於價值的判斷能力。 昨天看了一篇關於聯想和華為的文章,有些感觸。裡面提到一個觀點,聯想是產品驅動,華為是技術驅動,導致了多年後兩家公司的巨大反差。 這其中就有個在互聯網公司的常態化問題:一般都是產品汪來領導技術猿,產品經理多數是項目經理的角色,主要的精力只能用在搞 ...
  • while迴圈是PHP中最簡單的迴圈,其基本格式為: 該語法表示,只要expr表達式為TRUE,那麼就一直執行statement直到expr為FALSE為止,statement表示要執行的動作或邏輯。 該例子迴圈輸出1到10。 原文地址:http://www.manongjc.com/php/php ...
  • 為了方便直觀我們使用Head插件提供的介面進行演示,實際上內部調用的RESTful介面。 RESTful介面URL的格式: http://localhost:9200/<index>/<type>/[<id>] 其中index、type是必須提供的。 id是可選的,不提供es會自動生成。 index ...
  • #include<stdio.h> #include<stdlib.h> #include<time.h> double jieguo(); void main(){ int i,k; int m,n; int j; printf("請輸入購買的號碼:\n"); scanf("%d",&i); j= ...
  • 偶爾調試代碼的時候會出現這種事情,之前並沒有特別註意,今天稍微搜集一下相關資料: 1.跳轉到的代碼的確沒有源碼,下載源碼後選擇源碼位置後便會正常顯示源碼. 2.源碼和class文件不一致.即便勾選了auto build選項,eclipse依然存在class沒有實時編譯的情況,致使我們當前的代碼和編譯 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...