SpringBoot的自动配置是对Spring的一大增强,让我们从Spring的繁杂配置中解脱出来,同时这也是SpringBoot的老生常谈的话题了,接下来就来分析自动配置的源码,剖析它的原理
@EnableAutoConfiguration
众所周知,@SpringBootApplication由三个注解组成,一个是**@CompomentScan**,一个是**@SpringBootConfiguration**,还有一个就是**@EnableAutoConfiguration**
其中自动配置是在@EnableAutoConfiguration中完成的
java
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
在@EnableAutoConfiguration的上面,使用了@Import注解引入了一个新的类AutoConfigurationImportSelector
所以可以推测自动配置的逻辑也在AutoConfigurationImportSelector中
但是AutoConfigurationImportSelector与自动配置之间有什么关系呢?为啥自动配置要@Import它呢?@Import做了什么事情呢?@Import在什么时候生效呢?
ConfigurationClassPostProcessor
跟配置类有关的处理器是ConfigurationClassPostProcessor,被@Configuration修饰的配置类在这个处理器中处理并注册到BeanDefinitionMap中
ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor,同时也是BeanDefinitionRegistryPostProcessor,它会在invokeBeanFactoryPostProcessors执行invokeBeanFactoryPostProcessors和invokeBeanDefinitionRegistryPostProcessors
而自动配置的逻辑,就在ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry中,在这里调用了processConfigBeanDefinitions
以下代码是processConfigBeanDefinitions的一部分,这部分代码是用于处理被@Configuration修饰的类
java
// org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
candidates.clear();
// ...
while (!candidates.isEmpty());
我们关注这一行代码
java
parser.parse(candidates);
这里的candidates是一个包含主启动类的BeanDefinition的set(我这次的主启动类就是mybatisDemoApplication)
然后看看parse在做什么
java
// org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
// ...
}
// catch
}
this.deferredImportSelectorHandler.process();
}
由于主启动类的BeanDefinition属于AnnotatedGenericBeanDefinition,所以走这段逻辑
java
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
跟进
java
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
继续跟进
java
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// ...
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
下面这个方法就是真正在处理了
java
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// ...
// Process any @PropertySource annotations
// ...
// Process any @ComponentScan annotations
// ...
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
//...
// Process individual @Bean methods
// ...
// ...
// No superclass -> processing is complete
return null;
}
这个方法中有很多很重要的逻辑,包括我们熟悉的@ComponentScan和@Bean,但是我们先不管它们
我们直接看processImports
java
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// ...
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
// ...
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
// ...
}
// ...
}
// ...
}
还是关心主要的核心逻辑,别的逻辑我都去掉了,不然真的太乱了
这个importCandidates里面是这两个
也就是主启动类中import的两个类
我们关心AutoConfigurationImportSelector
java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
由于AutoConfigurationImportSelector属于DeferredImportSelector
所以会进入这一行
java
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
java
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
// ...
}
else {
this.deferredImportSelectors.add(holder);
}
}
这里deferredImportSelectors是非空的
可以看到DeferredImportSelector被放到deferredImportSelectors了
然后一路返回到之前的parse方法
java
// org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
// ...
}
// catch
}
this.deferredImportSelectorHandler.process();
}
java
// deferredImportSelectorHandler.process
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
java
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
// 递归处理
});
}
}
java
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
java
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);
}
}
然后来到了最末尾
java
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
java
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;
}
可以看到这里就是获取autoConfiguration
这个configurations是从哪里来的呢?
可以看到就是从META/spring.factories中加载进来的
但是其实真正符合条件的没有那么多
还要经过一层过滤
java
configurations = getConfigurationClassFilter().filter(configurations);
最后全部注册到BeanDefinitionMap里面
这里有79个是因为要处理那些AutoConfiguration中的@Configuration内部类
以上就是把自动配置的Bean加入到BeanDefinitionMap里面,至于什么时候生效呢?
在yml配置的属性会在ConfigurationPropertiesBindingPostProcessor中的postProcessBeforeInitialization去做属性的绑定
java
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
return bean;
}
总结
自动配置的链路还是比较长的,总结一下大致流程如下:
- springboot启动的时候,会通过loadSpringFactories把META/spring.factories中的AutoConfiguartion加载进来
- 在invokeBeanFactoryPostProcessor会将自动配置类那些AutoConfiguration过滤之后注册到BeanDefinitionMap中
- 在初始化bean时,通过ConfigurationPropertiesBindingPostProcessor的postProcessBeforeInitialization将yml的属性进行绑定