SpringBoot進階之道-@SpringBootApplication

相信小夥伴們在寫springboot項目的時候,在啓動類上加上@SpringBootApplication註解引導,就能夠自動裝配。例以下面這樣:java

@SpringBootApplication
public class SpringbootTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootTestApplication.class, args);
    }
}
複製代碼
  • 那麼@SpringBootApplication註解是怎麼作到的?
  • 什麼叫 @Component派生註解

一、理解@SpringBootApplication語義

引用官方的話:@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

二、@Component ‘派生性’

但咱們知道,從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 {
}
複製代碼

所以三者的關係爲:

  • @component
    • @Configuration
      • @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。

相關文章
相關標籤/搜索