SpringBoot 自动配置跟踪记录
从启动流程解析中可以看到,@SpringBootApplication注解中包含了@EnableAutoConfiguration,先看看这个注解的源码。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
可以看到,这个注解引入另一个类AutoConfigurationPackages.Registrar,功能是从主程序类所在的包路径(包括子包)下的所有组件注册到 Spring IOC 容器中。
再看 @Import(AutoConfigurationImportSelector.class)
引入类AutoConfigurationImportSelector,看看核心方法selectImports(),
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 1.得到注解信息
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 2.对注解信息进行处理
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
看getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解中的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 从注解数据中获取候选配置列表
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 排除不需要的信息
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
// 触发自动配置类引入完成的事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 创建 AutoConfigurationEntry 对象并返回
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations()是比较关键的方法,主要是从注解数据中获取候选配置列表
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
可以看到,调用的SpringFactoriesLoader.loadFactoryNames()方法,从 (所有jar下)META-INF/spring.factories
加载的指定类型为 EnableAutoConfiguration 类。
随便找到一个jar的spring.factories的文件,可以看到有很多自动配置的key-value属性:
点击其中一个配置类看看RedisAutoConfiguration,
@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
这里就放着redis相关配置。
从这里就可以得出,Springboot 在启动的时候会AutoConfigurationImportSelector去寻找jar包下面的META-INF/spring.factories文件,通过这些文件找到这些配置类的位置,并去加载其中的配置。当然这些配置类也不是全部加载到容器中,而是通过@ConditionalOnxxx的注解来判断是否满足条件,从而达到根据条件来加载对应类的功能。
以上只是大概的流程。