>>>>> 附3 springboot源碼解析 - 構建SpringApplication

來源:互聯網
上載者:User

標籤:對象   names   end   add   包含   src   res   hashset   output   

package com.microservice.framework;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class MySpringAplication {    public void run(String[] args) {        SpringApplication sa = new SpringApplication(MySpringAplication.class);        sa.run(args);    }}

SpringBoot啟動過程:

1、構建SpringApplication對象

2、執行run()

一、構建SpringApplication對象

     /**      * The application context will load beans from the specified sources       */     public SpringApplication(Object... sources) {         initialize(sources);     }

說明:

  • 執行個體化該類的時候會載入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列表

private List<ApplicationContextInitializer<?>> initializers;    public void setInitializers(            Collection<? extends ApplicationContextInitializer<?>> initializers) {        this.initializers = new ArrayList<ApplicationContextInitializer<?>>();        this.initializers.addAll(initializers);    }    private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {        return getSpringFactoriesInstances(type, new Class<?>[] {});    }    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        Set<String> names = new LinkedHashSet<String>(                SpringFactoriesLoader.loadFactoryNames(type, classLoader));        List<T> instances = new ArrayList<T>(names.size());        // Create instances from the names        for (String name : names) {            try {                Class<?> instanceClass = ClassUtils.forName(name, classLoader);                Assert.isAssignable(type, instanceClass);                Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);                T instance = (T) constructor.newInstance(args);                instances.add(instance);            }            catch (Throwable ex) {                throw new IllegalArgumentException(                        "Cannot instantiate " + type + " : " + name, ex);            }        }        AnnotationAwareOrderComparator.sort(instances);        return instances;    }

步驟:

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

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

/**     * The location to look for factories.     * <p>Can be present in multiple JAR files.     */    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";    /**     * Load the fully qualified class names of factory implementations of the     * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given     * class loader.     */    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {        String factoryClassName = factoryClass.getName();        try {            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));            List<String> result = new ArrayList<String>();            while (urls.hasMoreElements()) {                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 ex) {            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +                    "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);        }    }

META-INF/spring-factories

1 # Application Context Initializers2 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列表

private List<ApplicationListener<?>> listeners;            /**     * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication     * and registered with the {@link ApplicationContext}.     * @param listeners the listeners to set     */    public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {        this.listeners = new ArrayList<ApplicationListener<?>>();        this.listeners.addAll(listeners);    }

META-INF/spring-factories

# Application Listenersorg.springframework.context.ApplicationListener=org.springframework.boot.builder.ParentContextCloserApplicationListener,org.springframework.boot.context.FileEncodingApplicationListener,org.springframework.boot.context.config.AnsiOutputApplicationListener,org.springframework.boot.context.config.ConfigFileApplicationListener,org.springframework.boot.context.config.DelegatingApplicationListener,org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,org.springframework.boot.logging.ClasspathLoggingApplicationListener,org.springframework.boot.logging.LoggingApplicationListener

以上八個listener的作用如下:

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

 

1.5、初始化主類mainApplicationClass

private Class<?> mainApplicationClass;    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;    }

說明:擷取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 完成,該文的作者已經解析的很好了,我這裡再抄一遍,只是為了加深記憶!!!

>>>>> 附3 springboot源碼解析 - 構建SpringApplication

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.