相信小夥伴們在寫springboot項目的時候,在啓動類上加上@SpringBootApplication註解引導,就能夠自動裝配。例以下面這樣:java
@SpringBootApplication
public class SpringbootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTestApplication.class, args);
}
}
複製代碼
引用官方的話:@SpringBootApplication被用於激活@EnableAutoConfiguration
、@ComponentScan
和@Configuration
三個特性。其中,@EnableAutoConfiguration
負責激活SpringBoot自動裝配機制,@ComponentScan
激活@Component的掃描,@Configuration
聲明被標註爲配置類。官方文檔繼續告訴開發人員@SpringBootApplication註解等同於@Configuration
、@EnableAutoConfiguraion
和@ComponentScan
註解,且它們均使用默認屬性。咱們這樣改造上面的代碼:spring
//@SpringBootApplication
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class SpringbootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTestApplication.class, args);
}
}
複製代碼
咱們啓動運行,觀察日誌,沒錯,一切正如你想象的正常。springboot
額~ 相信有很多人翻過springBoot2.x的@SpringBootApplication的源碼,發現是下面這樣的:ide
@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 {
....
}
複製代碼
因而可知,那麼有個疑問?@SpringBootSpplication是否是不等價上面介紹的@EnableAutoConfiguration
、@ComponentScan
和@Configuration
註解?this
那麼咱們分析,由上可見,@SpringBootApplication等價於@SpringBootConfiguration
、@ComponentScan
和@EnableAutoConfiguration
,不過@ComponentScan並不是使用了默認值,而是添加了排除的TypeFilter實現:TypeExcludeFilter和AutoConfigurationExcludeFilter。前者由Springboot1.4引入,用於查找BeanFactory中已註冊的TypeExcludeFilter Bean,做爲代理執行對象:spa
public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {
...
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
if (this.beanFactory instanceof ListableBeanFactory && this.getClass() == TypeExcludeFilter.class) {
Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
Iterator var4 = delegates.iterator();
while(var4.hasNext()) {
TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
...
}
複製代碼
然後者從SpringBoot1.5開始支持,用於排除其餘同時標註@Configuration和@EnableAutoConfiguration的類:代理
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
...
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
}
return this.autoConfigurations;
}
}
複製代碼
咱們來對比一下SpringBoot1.3.8的@SpringBootApplication的聲明:日誌
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration和
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
....
}
複製代碼
因而可知,SpringBoot1.3.8的實現與官方文檔描述的是同樣的。儘管SpringBoot1.4以後的@SpringBootApplication一般不會表現出與文檔相異的行爲,但官方沒更新文檔也沒給出具體有什麼不同的解釋。code
但咱們知道,從SpringBoot1.4開始,@SpringBootApplication註解再也不標註@Configuration,而是@SpringBootConfiguration
,不過二者在運行上的行爲並無什麼不同,這種相似於對象之間的繼承關係,咱們稱之爲「多層次 @Component‘派生性’」
,哈哈哈,而且這種能力也容許咱們擴展使用的,是否是很嗨。咱們看下@Configuration註解,它其實標註了@Component註解:component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
...
}
複製代碼
因此咱們得知,@Configuration其實是@Component的派生性註解,同理@SpringBootConfiguration標註了@Configuration:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
複製代碼
所以三者的關係爲:
咱們知道,@CompoentScan僅掃描帶有@Component註解的類,並註冊成bean,然而因爲@SpringBootConfiguration屬於多層次的@Component「派生」註解,因此能被@CompoentScan識別。可是咱們知道@CompoentScan屬於Spring Framework,而@SpringBootConfiguration來自SpringBoot,那麼是什麼機制讓@CompoentScan可以識別@SpringBootConfiguration註解呢?這種機制就是前面提到的「多層次 @Component ‘派生性’」。
SpringBoot是經過註解@EnableAutoConfiguration的方式,去查找,過濾,加載所需的Configuration,@ComponentScan掃描咱們自定義的bean,@SpringBootConfiguration(派生性@Component)使得被@SpringBootApplication註解的類聲明爲註解類,所以@SpringBootApplication的做用等價於同時組合使用@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration。