接上文SpringBoot自动配置原理解析(三)后,我们本文开始分析this.reader.loadBeanDefinitions(configClasses);。也就是ConfigurationClassBeanDefinitionReaderloadBeanDefinitions方法。

也就是在这个方法里面,对前面没有处理的@Bean注解的method、配置类引入的 比如AutoConfigurationPackages.Registrar以及扫描得到的XXXXAutoConfiguration做了处理。

springboot禁止 log_lua

【1】ConfigurationClassBeanDefinitionReader

如下所示,首先实例化一个TrackedConditionEvaluator ,然后对每个ConfigurationClass 执行loadBeanDefinitionsForConfigurationClass方法。

// ConfigurationClassBeanDefinitionReader
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

① loadBeanDefinitionsForConfigurationClass

loadBeanDefinitionsForConfigurationClass方法如下所示:

// ConfigurationClassBeanDefinitionReader
private void loadBeanDefinitionsForConfigurationClass(
		ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

//判断是否跳过当前configClass,如果跳过则从registry importRegistry移除数据
	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}
// 如果当前类是通过@Import导入或者是某个配置类的内部类或者扫描得到的XXXAutoConfiguration
	if (configClass.isImported()) {
	// 注册configClass自身作为一个BeanDefinition放到beanDefinitionMap中
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	// 遍历配置类持有的BeanMethod,注册BeanDefinition到beanDefinitionMap中
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
//获取配置类持有的importedResources,解析配置文件加载BeanDefinition
//如果你使用了@ImportResource(locations = {"classpath:beans.xml"}),这里就会解析beans.xml文件
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	
//获取配置类持有的importBeanDefinitionRegistrars,触发其registerBeanDefinitions方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

梳理逻辑如下:

  • 判断是否跳过当前configClass,如果跳过则从registry importRegistry移除数据
  • 如果当前类是通过@Import导入或者是某个配置类的内部类或者是spring.factories扫描得到的xxxAutoConfiguration,则注册configClass自身作为一个BeanDefinition放到beanDefinitionMap中
  • 遍历配置类持有的BeanMethod,注册BeanDefinition到beanDefinitionMap中
  • 获取配置类持有的importedResources,解析配置文件加载BeanDefinition。如果你使用了@ImportResource(locations = {"classpath:beans.xml"}),这里就会解析beans.xml文件
  • 获取配置类持有的importBeanDefinitionRegistrars,触发其registerBeanDefinitions方法

② loadBeanDefinitionsFromRegistrars

loadBeanDefinitionsFromRegistrars方法如下所示,对每一个Entry触发registrar的registerBeanDefinitions方法。

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
	registrars.forEach((registrar, metadata) ->
			registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

我们以AutoConfigurationPackages.Registrar为例,这里会来到registerBeanDefinitions方法。

springboot禁止 log_实例化_02


我们继续往下看AutoConfigurationPackages的register方法,本文这里只是实例化得到一个GenericBeanDefinition 并注册到registry中。

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
	if (registry.containsBeanDefinition(BEAN)) {
		BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
		ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
		constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
	}
	else {
		GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
		beanDefinition.setBeanClass(BasePackages.class);
		beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(BEAN, beanDefinition);
	}
}

如下图所示,注册的beanName与beanDefinition。

springboot禁止 log_xml_03


也就是说这个方法以编程方式注册自动配置包名称。您可以使用此方法手动定义将用于给定BeanDefinitionRegistry的基本包。通常,建议不要直接调用此方法,而是依赖默认约定,其中包名是从@EnableAutoConfiguration配置类设置的。

【2】bean的实例化

如下所示,当我们do…while执行完毕后,我们有了442个BeanDefinition。

springboot禁止 log_springboot禁止 log_04


这些BeanDefinition在什么时候实例化呢?大部分在如下两个方法中:

//实例化BeanPostProcessor
registerBeanPostProcessors(beanFactory);

//实例化非懒加载的单例
finishBeanFactoryInitialization(beanFactory);