1.springboot启动过程中,首先会收集需要加载的bean的定义,作为BeanDefinition对象,添加到BeanFactory中去。
由于BeanFactory中只有 getBean 之类获取 bean 对象的方法,所以将将BeanDefinition添加到BeanFactory中,是通过 BeanDefinitionRegistry 接口的 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException; 方法来完成的。
所以我们的BeanFactory的实现类如果需要具备通过beanName来返回bean对象和添加删除BeanDefinition的能力,至少实现 BeanFactory 和 BeanDefinitionRegistry 的这两个接口。
这里我们就来看看springboot是如何查找bean的定义,添加到BeanFactory中的。
由于我们这里只是关注查找bean对象的定义,所以这里我们这里提到的BeanFactory主要会关注 BeanDefinitionRegistry 这个接口。
我们本地主要分析 springboot 扫描加载 bean 的配置,和我们的代码关系不大,所以我们的代码就用最简单的吧。具体代码如下:
package com.example.bootargs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BootargsApplication {
public static void main(String[] args) {
SpringApplication.run(BootargsApplication.class, args);
}
}
后面提到的主类统一是 com.example.bootargs.BootargsApplication
2.
Springboot 查找bean的定义主要是通过 ConfigurationClassPostProcessor 这个类来完成的。
ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口。 BeanDefinitionRegistryPostProcessor 接口就是通过 postProcessBeanDefinitionRegistry 方法来给 BeanDefinitionRegistry 的实现类来添加bean的定义。
BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor 接口,而 BeanFactoryPostProcessor 接口主要是用来对 BeanFactory 进行增强。在springboot启动过程中首先会创建 BeanFactory ,再调用 BeanFactoryPostProcessor 对 BeanFactory
进行增强,最后才会去创建 bean 对象。
通过 BeanFactoryPostProcessor 对BeanFactory进行增强,主要是通过 PostProcessorRegistrationDelegate 的静态方法来完成的。在这过程中就会调用到 ConfigurationClassPostProcessor 这个类。
由于 ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口, PostProcessorRegistrationDelegate 就会调用 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 方法中,就会调用到 processConfigBeanDefinitions 方法来查找bean的定义。我们就从这里作为入口来看吧。
3.
下面我们就去看看 ConfigurationClassPostProcessor 的 processConfigBeanDefinitions 方法
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
//在下面的这个for循环中,会从beanFactory中已经有的bean的定义中寻找有Configuration注解的配置类。
//默认这里获取到的只有一个包含SpringBootApplication注解的主类
for (String beanName : candidateNames) {
......
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//如果没有找到配置类,就直接返回
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
......
//在这里就通过ConfigurationClassParser去解析配置类
// 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");
//所有bean的定义的查找都是在这里完成的。下面我们去看看这里的parse方法
parser.parse(candidates);
......
}
在 ConfigurationClassParser 中的 parse 方法中,由于我们的配置类是通过注解来定义的,所以会走 AnnotatedBeanDefinition 这个分支。继续会调用到 processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); 这句,我们就直接进到这个 processConfigurationClass 方法去看吧。
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
//在这里首先看配置类上是否有Conditional注解,如果有的话,就去解析处理,看看是否要跳过这个注解类
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
//所有解析出来的配置类都要放置到configurationClasses中,key是当前解析出来的配置类,value就是表示这个配置类是通过谁来导入的。
//如果这个配置类不是通过别的类来导入的,这时key和value就是一样的。
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
//如果通过多个配置类导入了同一个配置类,那么把这个和配置类的导入关系就要进行一下合并
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
//这里是将配置类转化为SourceClass对象
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
//在这里就会进行真正的配置类的解析出来。
//注意这里是个do-while循环,处理完当前的配置类,会继续去处理当前配置类的父类。
//如果当前类的父类类名不是java开头,且没有被处理过的话,就会在这个do-while循环中继续去处理
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION) 这个的过滤主要是通过 org.springframework.context.annotation.Condition 接口的子类去实现 matches 方法完成的。
举个例子简单说下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration
上面是 MessageSourceAutoConfiguration 类的定义,首先会查找它上面的 Conditional 注解,会找到两个注解:
- @ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)由于这个这个注解上面有 @Conditional(OnBeanCondition.class) ,所以会交给 OnBeanCon