Spring @Bean 的处理流程是怎样的?

Spring @Bean 的处理流程是怎样的?

项目环境

1.@Bean 用法

本章我们使用 CircularReferencesDemo 类作为调试类

@Bean 是通过方法注入的方式将 bean 注入到 IoC 容器中,@Bean 必须是在 ConfigurationClass 中才能生效,示例如下:

@Configuration
public class CircularReferencesDemo {
    @Bean
    public Student student() {
        Student student = new Student();
        student.setId(1L);
        student.setName("小仙");
        return student;
    }
    ...

通过前面的 Spring 系列文章,我们可以知道不管是 XML、properties、@Component 以及其派生注解,或者是 @Bean 方式,最终都会被解析成一个 BeanDefinition 的元信息,注入到 IoC 容器中。

BeanDefinition 是什么?

这里我们可以看前面的文章 第五章:Spring Bean 定义,这里就不多加赘述;接下来我们看看 @Bean 的处理流程。

2.框架提前注册处理类

框架通过 AnnotationConfigUtils#registerAnnotationConfigProcessors() 方法主动去注册了这些处理的类

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor

我们看 ConfigurationClassPostProcessor 中的相关方法

  • ConfigurationClassPostProcessor#processConfigBeanDefinitions

3.解析类封装 @Bean 方法元信息

这里的 ConfigurationClass 在本示例中可以理解为 CircularReferencesDemo 类

解析 ConfigurationClass,并从类的信息中获取 @Bean 相关的方法信息

  • ConfigurationClassParser#doProcessConfigurationClass 319 行
		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

我们将断点打在 ConfigurationClassParser 320 行,看看 beanMethods 中的对象,如下图:
在这里插入图片描述
可以看到通过 retrieveBeanMethodMetadata 方法从 CircularReferencesDemo 中解析出来两个标注有 @Bean 的方法,分别是我们设置的 student 和 classRoom 方法,而 sourceClass 其实就是我们的 CircularReferencesDemo 示例本身。

继续往下,循环遍历 beanMethods 对象,将 methodMetadata 封装成 BeanMethod 对象,并添加到 configClass 对象中。
在这里插入图片描述

4.将元信息转化为 BeanDefinition

我们通过 IDEA 查找下 BeanMethod 被哪些源码使用
在这里插入图片描述
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

相关代码如下:

		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

从 configClass 获取相关的 BeanMethod 遍历循环,调用 loadBeanDefinitionsForBeanMethod 方法,看方法名称意思是通过 BeanMethod 加载 Bean 的定义。

那么我们将断点打到这个方法中,首先进来的是 student 方法
在这里插入图片描述
静态方法&实例方法

因为 @Bean 的注入方式是通过方法注入,所以需要区分静态方法和实例方法两种不同的场景

  • ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod

相关代码如下:

		if (metadata.isStatic()) {
			// static @Bean method
			if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
				beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
			}
			else {
				beanDef.setBeanClassName(configClass.getMetadata().getClassName());
			}
			beanDef.setUniqueFactoryMethodName(methodName);
		}
		else {
			// instance @Bean method
			beanDef.setFactoryBeanName(configClass.getBeanName());
			beanDef.setUniqueFactoryMethodName(methodName);
		}

静态方法直接设置 FactoryMethodName ,而实例方法需要先设置 FactoryBeanName,再设置 FactoryMethodName。

这里我们的 student 采用的是实例方法,解析之后的 BeanDefinition 信息如下:
在这里插入图片描述
最后通过 this.registry.registerBeanDefinition(beanName, beanDefToRegister); 将 BeanDefinition 元信息注册到 IoC 中。

5.总结

@Bean 注解的处理流程总结为以下 4 个步骤:

  • Spring 框架提前注册了相关的处理类;
  • ConfigurationClassPostProcessor 中会调用 ConfigurationClassParser 类来解析 ConfigurationClass 获取 @Bean 方法相关的元信息;
  • 将 @Bean 方法相关的元信息封装成 BeanMethod 对象,并添加到 configClass 对象中;
  • 再从 configClass 取出 BeanMethod 对象,并转化为 BeanDefinition,最后注册到 IoC 容器中。

6.参考

  • 极客时间-小马哥《小马哥讲Spring核心编程思想》
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值