1
头图

springboot与spring相比,有一个优势就是大部分配置都有一套默认设定,不必再写大量的配置文件,即“约定优于配置”。
而这是靠自动装配实现的。

自动装配流程:

  • 启动类注解@SpringBootApplication作为入口,找到@EnableAutoConfiguration->@Import(AutoConfigurationImportSelector.class)
  • AutoConfigurationImportSelector实现了接口DeferredImportSelector,核心方法为:

    public Class<? extends Group> getImportGroup() {
      return AutoConfigurationGroup.class;
    }

    具体实现指向了AutoConfigurationGroup

  • AutoConfigurationGroup实现了DeferredImportSelector.Group接口,有两个核心方法:

    • process

      • 通过spi方式读取配置(META-INF/spring.factories),加载对象
      • 注解中exclude属性指向的类排除
    • selectImports
      整合process方法结果,转换成entry返回——此方法会被refresh调用,这些entry最终会以BeanDefinition对象的形式,放入到BeanDefinitionMap中

接下来看看源码实现。

一、自动加载入口

springboot的启动方式如下:

@SpringBootApplication
public class SpringRunner {
    public static void main(String[] args) {
        SpringApplication.run(SpringRunner.class, args);
    }
}

观察@SpringBootApplication注解

// ## 此注解十分重要
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {...}


// ## 此注解同样重要
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}

从上面的代码看出,最终会通过@Import注解进入AutoConfigurationImportSelector类(调用链:@SpringBootApplication->@EnableAutoConfiguration->@Import(AutoConfigurationImportSelector.class))

继续观察:

// == 1.AutoConfigurationImportSelector实现DeferredImportSelector接
//      调用getImportGroup()方法,返回AutoConfigurationGroup类
public class AutoConfigurationImportSelector implements DeferredImportSelector{
    @Override
    public Class<? extends Group> getImportGroup() {
        return AutoConfigurationGroup.class;
    }
}

// == 2.实现DeferredImportSelector.Group接口    
private static class AutoConfigurationGroup
            implements DeferredImportSelector.Group{..}
}

Group接口有两个方法:

// 加载配置信息
void process(AnnotationMetadata metadata, DeferredImportSelector selector);

// 包装成Entry对象并返回
Iterable<Entry> selectImports();

接下来分别看一下这两个方法的实现。

1、process()方法——配置文件加载

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
  
  AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
      // == 配置加载
      .getAutoConfigurationEntry(annotationMetadata);

  this.autoConfigurationEntries.add(autoConfigurationEntry);
  for (String importClassName : autoConfigurationEntry.getConfigurations()) {
      this.entries.putIfAbsent(importClassName, annotationMetadata);
  }
}

// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 获取配置信息
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    
    //...
}

// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // ## spi方式加载配置
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
                getBeanClassLoader());
    return configurations;
}

之前介绍了spi配置加载,spi方式会查找META-INF下的spring.factories文件
文件内容:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
// ...省略...

根据org.springframework.boot.autoconfigure.EnableAutoConfiguration自动加载Redis、Mongo等156个默认配置

2、selectImports()方法——包装成Entry

@Override
public Iterable<Entry> selectImports() {
    // 配置排序,并包装成Entry返回
    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
            .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
            .collect(Collectors.toList());
}

二、spring初始化时加载Entry对象

Group接口selectImports()方法返回的Entry对象去了哪里?
其实spring初始化加载时会用到它们。

从spring的refresh方法(初始化核心方法)一路追踪,整个调用链如下:

AbstractApplicationContext # refresh
AbstractApplicationContext # invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate # invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor # postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor # processConfigBeanDefinitions
ConfigurationClassParser # parse
ConfigurationClassParser$DeferredImportSelectorHandler # process
ConfigurationClassParser$DeferredImportSelectorGroupingHandler # processGroupImports      
ConfigurationClassParser$DeferredImportSelectorGrouping # getImports
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports

这些Entry最终会以BeanDefinition对象的形式,放入到BeanDefinitionMap中。
后续进入spring bean对象初始化、属性赋值流程了,不再细说。


青鱼
268 声望25 粉丝

山就在那里,每走一步就近一些