SpringBoot 源碼解析 (五)----- Spring Boot的核心能力 - 自動配置源碼解析

在上一篇博客中分析了springBoot啓動流程,大致的輪廓只是冰山一角。今天就來看一下springBoot的亮點功能:自動化裝配功能。java

先從@SpringBootApplication開始。在啓動流程章節中,咱們講述了SpringBoot2大體的啓動步驟,並進行了源碼詳解。可是在刷新容器這塊並未展開,refreshContext(context);簡單的一行代碼,背後卻作了太多事情。因此爲了避免喧賓奪主,本篇也儘可能選取和註解@SpringBootApplication有關的方法講解。spring

springBoot啓動類加載

首先加載springBoot啓動類注入到spring容器中beanDefinitionMap中,看下prepareContext方法中的load方法:load(context, sources.toArray(new Object[0]));
跟進該方法最終會執行BeanDefinitionLoader的load方法:ide

private int load(Object source) {
    Assert.notNull(source, "Source must not be null");
    //若是是class類型,啓用註解類型
    if (source instanceof Class<?>) { return load((Class<?>) source); } //若是是resource類型,啓用xml解析
    if (source instanceof Resource) {
        return load((Resource) source);
    }
    //若是是package類型,啓用掃描包,例如:@ComponentScan
    if (source instanceof Package) {
        return load((Package) source);
    }
    //若是是字符串類型,直接加載
    if (source instanceof CharSequence) {
        return load((CharSequence) source);
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

繼續跟進load(Class<?> source)方法:spring-boot

上述方法判斷啓動類中是否包含@component註解,可咱們的啓動類並無該註解。繼續跟進會發現,AnnotationUtils判斷是否包含該註解是經過遞歸實現,註解上的註解若包含指定類型也是能夠的。this

啓動類中包含@SpringBootApplication註解,進一步查找到@SpringBootConfiguration註解,而後查找到@Component註解,最後會查找到@Component註解:url

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

在查找到@Component註解後,表面該對象爲spring bean,而後會將其信息包裝成 beanDefinitaion ,添加到容器的 beanDefinitionMap中。以下:spa

如此一來,咱們的啓動類就被包裝成AnnotatedGenericBeanDefinition了,後續啓動類的處理都基於該對象了。3d

@EnableAutoConfiguration

@SpringBootApplication註解中包含了自動配置的入口註解:code

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

咱們跟進去看看@EnableAutoConfigurationcomponent

@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage

  • 自動配置包註解
@Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {}

@Import(AutoConfigurationPackages.Registrar.class):默認將主配置類(@SpringBootApplication)所在的包及其子包裏面的全部組件掃描到Spring容器中。以下

@Order(Ordered.HIGHEST_PRECEDENCE)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
         //默認將會掃描@SpringBootApplication標註的主配置類所在的包及其子包下全部組件
        register(registry, new PackageImport(metadata).getPackageName());
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.<Object>singleton(new PackageImport(metadata));
    }
}

@Import(EnableAutoConfigurationImportSelector.class)

EnableAutoConfigurationImportSelector: 導入哪些組件的選擇器,將全部須要導入的組件以全類名的方式返回,這些組件就會被添加到容器中。

 1 //EnableAutoConfigurationImportSelector的父類:AutoConfigurationImportSelector
 2 @Override
 3 public String[] selectImports(AnnotationMetadata annotationMetadata) {
 4     if (!isEnabled(annotationMetadata)) {
 5         return NO_IMPORTS;
 6     }
 7     try {
 8         AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
 9             .loadMetadata(this.beanClassLoader);
10         AnnotationAttributes attributes = getAttributes(annotationMetadata);
11         List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); 12         configurations = removeDuplicates(configurations);
13         configurations = sort(configurations, autoConfigurationMetadata);
14         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
15         checkExcludedClasses(configurations, exclusions);
16         configurations.removeAll(exclusions);
17         configurations = filter(configurations, autoConfigurationMetadata);
18         fireAutoConfigurationImportEvents(configurations, exclusions);
19         return configurations.toArray(new String[configurations.size()]);
20     }
21     catch (IOException ex) {
22         throw new IllegalStateException(ex);
23     }
24 }

咱們主要看第11行List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);會給容器中注入衆多的自動配置類(xxxAutoConfiguration),就是給容器中導入這個場景須要的全部組件,並配置好這些組件。獲取這些組件後,還要過濾一下這些組件,咱們跟進去看看

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    //...
    return configurations;
}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
        //從類路徑的META-INF/spring.factories中加載全部默認的自動配置類
        Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        List<String> result = new ArrayList<String>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            //獲取EnableAutoConfiguration指定的全部值,也就是EnableAutoConfiguration.class的值
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

SpringBoot啓動的時候從類路徑下的 META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,並將這些值做爲自動配置類導入到容器中,自動配置類就會生效,最後完成自動配置工做。EnableAutoConfiguration默認在spring-boot-autoconfigure這個包中,以下圖

最終有96個自動配置類被加載並註冊進Spring容器中

咱們也能夠將須要自動配置的Bean寫入這個文件

自定義starter

首先定義一個配置類模塊:

@Configuration
@ConditionalOnProperty(name = "enabled.autoConfituration", matchIfMissing = true) public class MyAutoConfiguration {

    static {
        System.out.println("myAutoConfiguration init...");
    }

    @Bean
    public SimpleBean simpleBean(){
        return new SimpleBean();
    }

}

而後定義一個starter模塊,裏面無需任何代碼,pom也無需任何依賴,只需在META-INF下面建一個 spring.factories文件,添加以下配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
spring.study.startup.bean.MyAutoConfiguration

如圖所示:

最後只需在啓動類項目的pom中引入咱們的 starter 模塊便可。

原理

最終在AutoConfigurationImportSelector解析spring.factories文件:

springBoot爲咱們提供的配置類有180多個,可是咱們不可能會所有引入。按條件註解 @Conditional或者@ConditionalOnProperty等相關注解進行判斷,決定是否須要裝配。

咱們自定義的配置類也是以相同的邏輯進行裝配,咱們指定了如下註解:

@ConditionalOnProperty(name = "enabled.autoConfituration", matchIfMissing = true)

默認爲 true,因此自定義的starter成功執行。

相關文章
相關標籤/搜索