springboot系列文章之SpringBootApplication註解

pjmike_pj發表於2018-08-18

前言

Springboot的啟動類可以是非常簡單,其中最關鍵的兩部分是Annotation定義(@SpringBootApplication)和類定義(SpringApplication.run),這篇文章主要分析其@SpringBootApplication註解,後續文章再接著分析其類定義。

@SpringBootApplication
public class Application {


    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
複製程式碼

其中參考了《SpringBoot揭祕》裡面的分析,也是學習總結了。

@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 {
    .....
}
複製程式碼

看似有這麼多註解,實際上@SpringBootApplication是一個"三體"結構,重要的只有三個Annotation:

  • @Configuration
  • @EnableAutoConfiguration
  • @ComponentScan

為什麼@SpringBootApplication註解裡沒有包含@Configuration,實際上是在@SpringBootConfiguration裡面

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
複製程式碼

@SpringBootConfiguration繼承自@Configuration,二者功能也一致,標註當前類是配置類, 並會將當前類內宣告的一個或多個以@Bean註解標記的方法的例項納入到spring容器中,並且例項名就是方法名

實際上如果我們使用如下的Springboot啟動類,整個SpringBoot應用依然可以與之前的啟動類功能對等。

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {


    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
複製程式碼

下面分別說說這三個關鍵註解

@Configuration

這裡的@Configuration就是以JavaConfig形式的Spring IoC容器的配置類使用的那個@Configuration,所以這裡的啟動類標註了@Configuration之後,本身其實也是一個IoC容器的配置類。

以前的XML配置是這樣的:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-lazy-init="true">
    <!--bean定義-->
</beans>
複製程式碼

現在的JavaConfig配置是這樣的,效果等同,標註了該註解說明就是一個配置類

@Configuration
public class Application{
    //bean定義
}
複製程式碼

更多關於@Configuration@Bean的講解,參考文章: 使用 Java 配置進行 Spring bean 管理

@ComponentScan

@ComponentScan的功能其實就是自動掃描並載入符合條件的元件或bean定義,最終將這些bean定義載入到容器中。我們可以通過basePackages等屬性指定@ComponentScan自動掃描的範圍,如果不指定,則預設Spring框架實現從宣告@ComponentScan所在類的package進行掃描,預設情況下是不指定的,所以SpringBoot的啟動類最好放在root package下。

@EnableAutoConfiguration

各位是否還記得Spring框架提供的各種名字為@Enable開頭的Annotation定義?比如@EnableScheduling,@EnableCaching,@EnableMBeanExport等,@EnableAutoConfiguration的理念和"做事方式"其實一脈相承,藉助@Import的支援,收集和註冊特定場景相關的bean定義:

  • @EnableScheduling是通過@Import將Spring排程框架相關的bean定義都載入到Ioc容器中。
  • @EnableMBeanExport是通過@Import將JMX相關的bean定義載入到Ioc容器

而@EnableAutoConfiguration也是藉助@Import的幫助,將所有符合自動配置條件的bean定義載入到Ioc容器,僅此而已

@EnableAutoConfiguration也是一個複合Annotation,其定義如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}
複製程式碼

其中,最關鍵的要屬@Import(AutoConfigurationImportSelector.class),藉助AutoConfigurationImportSelector這個類,@EnableAutoConfiguration可以幫助Springboot應用將所有符合條件的@configuration都載入到當前的SpringBoot建立並使用的Ioc容器

springboot

AutoConfigurationImportSelector

在SpringBoot1.5以前,使用的是EnableAutoConfigurationImportSelector,它繼承自AutoConfigurationImportSelector,1.5以後,EnableAutoConfigurationImportSelector已經不再被建議使用,而是推薦使用AutoConfigurationImportSelector

何時被執行

現在我們已經知道了在@EnableAutoConfiguration中引入了AutoConfigurationImportSelector類,那麼它是如何被執行的呢?

Springboot啟動時會使用ConfigurationClassParser來解析被@Configuration修飾的配置類,然後再處理這個類內部被其他註解修飾的情況,比如@Import註解,@ComponentScan註解,@Bean註解等。

如果發現註解中存在@Import(ImportSelector)的情況下,就會建立一個相應的importSelector物件,並呼叫其selectImports方法,而AutoConfigurationImportSelector就是一個ImportSelector的實現類。更多關於ConfigurationClassParser的分析,參閱文章:Spring類註冊筆記

selector
所以ConfigurationClassParser會例項化一個AutoConfigurationImportSelector 並呼叫它的 selectImports() 方法
get
在selectImports方法中有使用getCandidateConfigurations()這個方法,這個方法走進去,就可以看到自動配置的幕後英雄:SpringFactoriesLoader
loader

自動配置的幕後英雄:SpringFactoriesLoader

SpringFactoriesLoader的主要功能就是從指定的配置檔案META/spring.factories載入配置,spring.factories是一個典型的java properties檔案,配置格式為Key-Value形式,只不過Key和Value都是Java型別的完整類名。

進入loadFactoryNames()方法,就發現loadFactoryNames()讀取了ClassPath下面的 META-INF/spring.factories 檔案。

load
在@EnableAutoConfiguration的場景中,它更多是提供一種配置查詢的功能支援,即根據@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作為查詢的key,獲取對應的一組@Configuration類
auto

SpringBootApplication註解中4個方法

@SpringBootApplication不僅包括上面的三個重要註解,還包含有4個方法:

method

  • Class<?>[] exclude() default {}; 根據Class來排除特定的類加入Spring容器,傳入引數是class型別
  • String[] excludeName() default {}; 根據Class name排除特定的類加入spring容器,傳入引數是class的全類名字串陣列
  • String[] scanBasePackages() default {};指定掃描包,引數是包名的字串陣列
  • Class<?>[] scanBasePackageClasses() default {};指定掃描包,引數是Class型別陣列

小結

這裡總結下@SpringBootApplication中的三個重要註解的特徵:

  • @Configuration

定義Spring Ioc容器的配置類

  • @EnableAutoConfiguration:

從classpath中搜尋所有META/spring.factories配置檔案,並將其中org.springframework.boot.autoconfigure.EnableAutoConfiguration對應的配置項,也就是一個自動配置類列表載入到Ioc容器中。 簡單說,就是@EnawebleAutoConfiguration讓Spring Boot根據類路徑下的jar包依賴為當前專案進行自動配置,例如,新增了spring-boot-starter-web依賴,會自動新增Tomcat和Spring MVC的依賴。而對於所有標註@Configuration的配置類,統一使用ConfigurationClassParser解析的。

  • @ComponentScan

自動掃描並載入符合條件的元件或者bean定義

參考資料

相關文章