栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

@Bean注释的方法在Spring中的解析过程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

@Bean注释的方法在Spring中的解析过程

前言

本文使用的SpringBoot环境,默认使用的是AnnotationConfigApplicationContext上下文对象

一般我们使用@Bean一般都写在@Configuration注释的类下,Spring会帮我们将@Bean注释的方法返回的对象添加进容器中
如:

@Configuration
public class MyMapperConfig {

    @Bean
    public Car car(){
        return new Car();
    }

}
Tip

如果看过Spring源码或者已经了解扫描注解的过程可以直接看源码,下面我指出了几个位置

封装BeanMethod位置

ConfigurationClassParser 的 doProcessConfigurationClass() 方法。此方法将类的方法封装为BeanMethod

BeanMethod转换为BeanDefinition

ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitionsForBeanMethod() 方法

如何搜索到@Bean注释的方法?

Spring源码一般都会从refresh方法开始调试
在AbstractApplicationContext 的refresh()方法中会执行如下代码,注册BeanDefinition

  // Invoke factory processors registered as beans in the context.
  // 调用在上下文中注册为 bean 的工厂处理器
  invokeBeanFactoryPostProcessors(beanFactory);

由于context默认注入了ConfigurationClassPostProcessor 类,会进入processConfigBeanDefinitions()方法,该方法中会使用ConfigurationClassParser类的parse方法进行解析

// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set candidates = new linkedHashSet<>(configCandidates);
		Set alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			// 解析
			parser.parse(candidates);
			parser.validate();

			Set 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);

parse()方法最终会调用到本类(ConfigurationClassParser) 的doProcessConfigurationClass() 方法,此时看到我们常见的 @Component , @ComponentScan 等注解,下面展示了该方法的部分代码

if (configClass.getmetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getmetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getmetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		// 处理任何@ComponentScan 注释
		Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getmetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionevaluator.shouldSkip(sourceClass.getmetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				// 调用ComponentScanAnnotationParser类的parse方法根据配置解析带有@Component的类
				// 注意@Configuration中也含有@Component
				Set scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getmetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				// 检查扫描的定义集以获取任何进一步的配置类,并在需要时递归解析
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					// 检查是否带有@Configuration注解
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @import annotations
		processimports(configClass, sourceClass, getimports(sourceClass), filter, true);

		// Process any @importResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getmetadata(), importResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addimportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set beanMethods = retrieveBeanMethodmetadata(sourceClass);
		for (Methodmetadata methodmetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodmetadata, configClass));
		}

上面代码中处理任何@ComponentScan 注释时会默认扫描入口类所在的包及其子包,扫描带有@Component的类。检查扫描后的定义集,以获取任何进一步的配置类,并在需要时递归解析。
在parse方法中执行到下面这段代码时,将会处理单个 @Bean 方法,将@Bean注释的方法添加进配置类中。

        // Process individual @Bean methods
        //retrieveBeanMethodmetadata方法 检索所有 @Bean方法的元数据
		Set beanMethods = retrieveBeanMethodmetadata(sourceClass);
		for (Methodmetadata methodmetadata : beanMethods) {
			// 将其封装为BeanMethod并添加进配置类中
			configClass.addBeanMethod(new BeanMethod(methodmetadata, configClass));
		}

ConfigurationClassPostProcessor 解析完后,从配置类中加载BeanDefinition

Set 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);

从BeanMethod转换为BeanDefinition的过程可以参考 ConfigurationClassBeanDefinitionReader 的loadBeanDefinitionsForBeanMethod()方法
最后Spring容器会实例化这些BeanDefinition

本人能力有限,如有错误望大神指正。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/735064.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号