SpringBoot:運行原理探究

西部開源-秦疆老師:基於SpringBoot 2.1.6 的博客教程web

秦老師交流Q羣號: 664386224spring

未受權禁止轉載!編輯不易 , 轉發請註明出處!防君子不防小人,共勉!springboot

SpringBoot:運行原理探究

pom.xml

咱們以前寫的HelloSpring,究竟是怎麼運行的呢less

咱們來看pom.xml文件spring-boot

進入父項目,這裏纔是真正管理SpringBoot應用裏面全部依賴版本的地方,SpringBoot的版本控制中心;this

之後咱們導入依賴默認是不須要寫版本;可是若是導入的包沒有在依賴中管理着就須要手動配置版本了;url

啓動器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

springboot-boot-starter:就是spring-boot的場景啓動器spa

這裏的 spring-boot-starter-web 幫咱們導入了web模塊正常運行所依賴的組件;命令行

SpringBoot將全部的功能場景都抽取出來,作成一個個的starter (啓動器),只須要在項目中引入這些starter便可,全部相關的依賴都會導入進來 , 咱們要用什麼功能就導入什麼樣的場景啓動器便可 ;3d

 主程序

package com.kuang.springbootdemo02;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//@SpringBootApplication 來標註一個主程序類 , 說明這是一個Spring Boot應用
@SpringBootApplication
public class SpringbootDemo02Application {

    public static void main(String[] args) {
        //將Spring應用啓動起來
        SpringApplication.run(SpringbootDemo02Application.class, args);
    }

}

一個簡單的啓動類並不簡單!

SpringBootApplication

@SpringBootApplication :SpringBoot應用標註在某個類上說明這個類是SpringBoot的主配置類 , SpringBoot就應該運行這個類的main方法來啓動SpringBoot應用;進入這個註解:



 @ComponentScan

 這個註解在Spring中很重要 , 它對應XML配置中的元素。@ComponentScan的功能就是自動掃描並加載符合條件的組件或者bean , 將這個bean定義加載到IOC容器中 ;

@SpringBootConfiguration 

SpringBoot的配置類 ;標註在某個類上 , 表示這是一個SpringBoot的配置類;咱們繼續進去這個註解查看

@Configuration:配置類上來標註這個註解 ,配置類-----配置文件;咱們繼續點進去,發現配置類也是容器中的一個組件。@Component

 

EnableAutoConfiguration

 咱們回到 SpringBootApplication 註解中繼續看。

@EnableAutoConfiguration :開啓自動配置功能

之前咱們須要配置的東西,SpringBoot能夠自動幫咱們配置 ; @EnableAutoConfiguration告訴SpringBoot開啓自動配置功能,這樣自動配置才能生效;

咱們點擊去查看

 

@AutoConfigurationPackage : 自動配置包 , 點進去看到一個 @Import({Registrar.class})

 

@import :Spring底層註解@import , 給容器中導入一個組件 ,導入的組件由 {Registrar.class} 將主配置類 【即@SpringBootApplication標註的類】的所在包及包下面全部子包裏面的全部組件掃描到Spring容器 ;

而後回來繼續看:@Import({AutoConfigurationImportSelector.class}) :給容器導入組件 ;

 AutoConfigurationImportSelector : 導入哪些組件的選擇器 ;

它將全部須要導入的組件以全類名的方式返回 , 這些組件就會被添加到容器中 ;

它會給容器中導入很是多的自動配置類 (xxxAutoConfiguration), 就是給容器中導入這個場景須要的全部組件 , 並配置好這些組件 ;

有了自動配置類 , 免去了咱們手動編寫配置注入功能組件等的工做;

//AutoConfigurationImportSelector中獲取配置文件信息;
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}

SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguation.class,classLoader);

這裏的getSpringFactoriesLoaderFactoryClass()方法返回的就是咱們最開始看的啓動自動導入配置文件的註解類;

 

進入 SpringFactoriesLoader 查看;

找到對應的方法:發現最終會去讀取一個配置文件 : META-INF/Spring.factories 的文件 。 

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

 

咱們打開spring.factories的配置文件 , 看到了不少自動配置的文件;這就是自動配置根源所在!

SpringBoot在啓動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值做爲自動配置類導入容器 , 自動配置類就生效 , 幫咱們進行自動配置工做;

之前咱們須要本身配置的東西 , 自動配置類都幫咱們解決了

整個J2EE的總體解決方案和自動配置都在springboot-autoconfigure的jar包中;

咱們找一個打開看看 : WebMvcAutoConfiguration

 

因此,真正實現是從classpath中搜尋全部的META-INF/spring.factories配置文件 ,並將其中對應的 org.springframework.boot.autoconfigure. 包下的配置項經過反射實例化爲對應標註了 @Configuration的JavaConfig形式的IOC容器配置類 , 而後將這些都彙總成爲一個實例並加載到IOC容器中。

Run

我最初覺得就是運行了一個main方法,沒想到卻開啓了一個服務;

@SpringBootApplication
public class SpringbootDemo02Application {

    public static void main(String[] args) {
//該方法返回一個ConfigurableApplicationContext對象
//參數一:應用入口的類 參數類:命令行參數 SpringApplication.run(SpringbootDemo02Application.
class, args); } }

 SpringApplication.run分析

分析該方法主要分兩部分,一部分是SpringApplication的實例化,二是run方法的執行;

SpringApplication的實例化

1. 推斷應用的類型是普通的項目仍是Web項目

2.查找並加載全部可用初始化器 , 設置到initializers屬性中

3.找出全部的應用程序監聽器,設置到listeners屬性中

4.推斷並設置main方法的定義類,找到運行的主類

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

 

run方法的執行

相關文章
相關標籤/搜索