Springboot的工作機制:2 @SpringBootApplication背後的祕密

@SpringBootApplication是一個“三體”結構,實際上它是一個複合Annotation:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
...
}

雖然它的定義使用了多個Annotation進行元信息標註,但實際上對於SpringBoot應用來說,重要的只有三個Annotation,而“三體”結構實際上指的就是這三個Annotation:

  • @Configuration
  • @EnableAutoConfiguration
  • @ComponentScan

所以,如果我們使用如下的SpringBoot啓動類,整個SpringBoot應用依然可以與之前的啓動類功能對等:

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

但每次都寫三個Annotation顯然過於繁瑣,所以寫一個@SpringBootApplication這樣的一站式複合Annotation顯然更方便寫。

@Configuration創世紀

這裏的@Configuration對我們來說並不陌生,它就是JavaConfig形式的Spring IoC容器的配置類使用的那個@Configuration。
SpringBoot應用骨子裏就是一個Spring應用,自然也需要加載某個IoC容器的配置,又由於SpringBoot社區推薦使用基於JavaConfig的配置形式,所以,這裏的啓動類標註了@Configuration,啓動類本身也是一個IoC容器的配置類了!

很多SpringBoot的代碼示例都喜歡在啓動類上直接標註@Configuration或者@SpringBootApplication,對於初接觸SpringBoot的開發者來說,其實這種做法不便於理解,如果我們將上面的SpringBoot啓動類拆分爲兩個獨立的Java類,整個形勢就明朗了:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class DemoConfiguration{
	@Bean
	public Controller controller(){
		return new Controller();
	}
}
public class DemoApplication{
	public static void main(String args[] ){
		SpringApplication.run(DemoConfiguration.class,args);
	}
}

所以,啓動類DemoApplication其實就是一個標準Standalone類型Java程序的main函數啓動類,沒有什麼特殊的。
而@Configuration標註的DemoConfiguration定義其實也是一個普通的JavaConfig形式的IoC容器配置類,沒有啥新東西,全是Spring框架裏的概念!

@EnableAutoConfiguration的功效

@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
@AutoConfigrationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration{
	...
}

其中,最關鍵的要屬@Import(EnableAutoConfigurationImportSelector.class),借EnableAutoConfigurationImportSelector.class,@EnableAutoConfiguration可以幫助SpringBoot應用將所有符合條件的@Configuration配置都加載到當前SpringBoot創建並使用IoC容器,就跟一隻“八爪魚”一樣
在這裏插入圖片描述
藉助於Spring框架原有的一個工具類:SpringFActoriesLoader的支持,@EnableAutoConfiguration可以“智能”地自動配置功效才得以大功告成!

自動配置的幕後英雄:SpringFactoriesLoader詳解

SpringFactoriesLoader屬於Spring框架私有的一種擴展方案(類似於Java的SPI方案java.util.ServiceLoader),其主要功能就是從指定的配置文件META-INF/spring.factories加載配置,spring.factories是一個典型的java properties文件,配置的格式爲key=value形式,只不過Key和Value都是Java類型的完整類名(Fully qualified name),比如:

example.MyService=example.MyServiceImpl1,example.MyServiceImpl2

然後框架就可以根據某個類型作爲key來查找對應的類型名稱列表了:

public abstract class SpringFactoriesLoader{
	//...
	public static <T> List<T> loadFactories(...
	}
	public static List<String> loadFactoryNames(Class<?> factoryClass,ClassLoader classLoader){
		...
	}
	//...
}

對於@EnableAutoConfiguration來說,SpringFactoriesLoader的用途稍微不同一些,原來其本意是爲了提供SPI擴展的場景,而在@EnableAutoConfiguration的場景中,它更多是提供了一種配置查找的功能支持,即根據@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作爲查找的Key,獲取對應的一組@Configuration類:

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.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
...

以上是從SpringBoot的autoconfigure依賴包中的META-INF/spring.factories配置文件中摘錄的一段內容,可以很好地說明問題。
所以,@EnableAutoConfiguration自動配置的魔法其實就變成了:從classpath中搜尋所有META-INF/spring.factories配置文件,並將其中org.springframework.boot.autoconfigure.EnableAutoConfiguration對應的配置通過反射(Java Reflection)實例化爲對應的標註了@Configuration的JavaConfig形式的IoC容器配置類,然後彙總爲一個並加載到IoC容器。

目前爲止,還是Spring框架的原有概念和支持,依然麼有“新鮮事”!

可有可無的@ComponentScan

爲啥說@ComponentScan是可有可無的?因爲原則上來說,作爲Spring框架裏的“老一輩革命家”,@ComponentScan的功能其實就是自動掃描並加載符合條件的組件或bean定義,最終將這些bean定義加載到容器中。加載bean定義到Spring的IoC容器,我們可以手工單個註冊,不一定非要通過批量的自動掃描完成,所以說@ComponentScan是可有可無的。不過我們項目中很少不使用@Component 、@Service、@Repository的

對於SpringBoot應用來說,同樣如此,比如下面的啓動類:

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

如果我們當前應用沒有任何bean定義需要通過@ComponentScan加載到當前SpringBoot應用對應使用的IoC容器,那麼,除去@ComponentScan的聲明,當前SpringBoot應用依然可以照常運行,功能對等!
看,還是沒有啥新東西!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章