springboot自动扫描添加的BeanDefinition源码解析

本文深入探讨了Spring Boot启动过程中如何查找并添加BeanDefinition到BeanFactory。主要涉及ConfigurationClassPostProcessor类,它通过BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法来处理。配置类的解析和条件过滤是通过Condition接口的实现类完成的,如OnBeanCondition和ResourceBundleCondition。整个流程包括递归处理配置类、处理Import注解、ComponentScans扫描、处理Bean注解等,最终将所有符合条件的BeanDefinition添加到BeanFactory。
摘要由CSDN通过智能技术生成

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值