SpringBoot原理發現(一)

swayer發表於2020-11-02

 

說明:

本系列基於SpringBoot 2.2.9.RELEASE 版本,對SpringBoot的原理進行分析,一共分為四節:

SpringBoot原理發現(一):建立Hello World,對pom依賴以及@SpringBootApplication註解進行分析

SpringBoot原理發現(二):分析SpringBoot自動配置原理

SpringBoot原理發現(三):通過主配置類main方法分析SpringBoot啟動配置原理

SpringBoot原理發現(四):瞭解SpringBoot啟動中的幾個重要回撥機制

 

Hello World 

建立Hello World,主啟動類如下:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @RestController
    public class HelloController{

        @GetMapping("/")
        public String helloWorld(){
            return "hello world";
        }
    }
}

 

1. POM檔案

 1.1 父POM

<!-- 當前專案pom -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.9.RELEASE</version>
    <relativePath/> 
</parent>

<!-- spring-boot-starter-parent 的父pom -->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.2.9.RELEASE</version>
  <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

  可以看出當前專案pom中的parent 依賴了 spring-boot-dependencies,而spring-boot-dependencies中管理的就是SpringBoot應用裡面的所有依賴版本。(如果其中沒包含,需要自定義在父pom中進行依賴)

<!-- spring-boot-dependencies 部分程式碼 -->
<properties>
    <activemq.version>5.15.13</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.81</appengine-sdk.version>
    <artemis.version>2.10.1</artemis.version>
    <aspectj.version>1.9.6</aspectj.version>
    <assertj.version>3.13.2</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.0.3</awaitility.version>
    <bitronix.version>2.1.4</bitronix.version>
    
  .......

 1.2  啟動器

    SpringBoot將所有的功能場景都抽取成一個個的starter,只需要依賴某個starter,便有了相關依賴資訊及功能,這樣的starter稱之為場景啟動器。

  可以通過點選官網進行檢視包含哪些starter,如下圖:

  

  如開發web應用,只需要引入spring-boot-starter-web便有了web模組的相關功能

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

   spring-boot-starter-web:管理了web模組需要引用的依賴檔案,如下圖:

<!-- spring-boot-starter-web.pom部分依賴-->
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.2.9.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.2.9.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.2.9.RELEASE</version> <scope>compile</scope> </dependency>

 

 2.主程式分析

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @RestController
    public class HelloController{

        @GetMapping("/")
        public String helloWorld(){
            return "hello world";
        }
    }
}

  2.1 @SpringBootApplication

   該註解標註在某個類上,說明這個類是SpringBoot的主配置類,SpringBoot應用應該使用這個類的main方法進行啟動。

   進入該註解,可以看到@SpringBootApplication是一個組合註解,程式碼如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

   2. 1.1 @SpringBootConfiguration

    SpringBoot的配置類。進去會發現其實就是一個Spring的配置類@Configuration,而@Configuration其實就是一個元件@Component。

    @SpringBootConfiguration是SpringBoot提供的,而@Configuration是Spring提供的

    程式碼如下:

/**
* SpringBootConfiguration
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

.....

/**
* Configuration
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

  2 .2 @EnableAutoConfiguration

    作用:開啟自動配置功能

    所謂開啟自動配置:以前需要提供各種配置檔案,如包掃描等,現在使用@EnableAutoConfiguration標註,SpringBoot就會開啟自動配置功能。

    點開這個註解,會發現它同樣是組合註解,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    2.2.1 @AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}
@Import(AutoConfigurationPackages.Registrar.class)向容器中匯入一個元件,元件由@Import後面傳入的類指定
AutoConfigurationPackages.Registrar.class 關鍵程式碼如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImport(metadata).getPackageName());
        }

  通過debug可以檢視該註解是標註是在主啟動類上,獲取主啟動類所在的包位置路徑,將該路徑下面的所有包掃描注入到容器中。類似spring配置檔案xml中 <context:component-scan base-package="xx.yyy" />功能。

       @AutoConfigurationPackage 其實就是自動獲取主配置類下面的所有包並掃描注入進IOC容器中。

 

    2.2.2 @Import(AutoConfigurationImportSelector.class)

    同樣是往IOC容器中匯入AutoConfigurationImportSelector.class中指定的元件。

    AutoConfigurationImportSelector主要程式碼:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

     通過debug可以看出,它會返回各種XXXAutoConfiguration。而有了這些XXXAutoConfiguration,就不需要我們在手動去編寫相關場景的注入功能元件等工作,如下圖:

這些XXXAutoConfiguration又是從何而來呢?
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(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader())
 第一個引數:EnableAutoConfiguration.class
 第二個引數:ClassLoader

 進入loadFactoryNames方法,SpringBoot會掃描類路徑下的META-INF/spring.factories 檔案中定義的資料,並返回第一個引數EnableAutoConfiguration指定的值,並將這些值作為自動配置類匯入到容器中,
 在後續該方法會經常出現,用於掃描spring.factories中定義的值,如下圖:

  

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\

......

  至此基本上可以看出SpringBoot是如何進行載入且工作的。說到底spring的配置檔案一個也沒少,只是SpringBoot底層將我們的配置檔案進行自動配置到容器中了,少了手動編寫大量的元件配置。

 

相關文章