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成功执行。

相关文章
相关标签/搜索