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

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

文章目录

  • Spring @Bean 的处理流程是怎样的?
    • 项目环境
    • 1.@Bean 用法
    • 2.框架提前注册处理类
    • 3.解析类封装 @Bean 方法元信息
    • 4.将元信息转化为 BeanDefinition
    • 5.总结
    • 6.参考

项目环境

  • jdk 1.8
  • spring 5.2.2.RELEASE
  • github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
    • 本章模块:questions

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 中的对象,如下图:
Spring @Bean 的处理流程是怎样的?_第1张图片
可以看到通过 retrieveBeanMethodMetadata 方法从 CircularReferencesDemo 中解析出来两个标注有 @Bean 的方法,分别是我们设置的 student 和 classRoom 方法,而 sourceClass 其实就是我们的 CircularReferencesDemo 示例本身。

继续往下,循环遍历 beanMethods 对象,将 methodMetadata 封装成 BeanMethod 对象,并添加到 configClass 对象中。
Spring @Bean 的处理流程是怎样的?_第2张图片

4.将元信息转化为 BeanDefinition

我们通过 IDEA 查找下 BeanMethod 被哪些源码使用
Spring @Bean 的处理流程是怎样的?_第3张图片
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

相关代码如下:

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

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

那么我们将断点打到这个方法中,首先进来的是 student 方法
Spring @Bean 的处理流程是怎样的?_第4张图片
静态方法&实例方法

因为 @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 信息如下:
Spring @Bean 的处理流程是怎样的?_第5张图片
最后通过 this.registry.registerBeanDefinition(beanName, beanDefToRegister); 将 BeanDefinition 元信息注册到 IoC 中。

5.总结

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

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

6.参考

  • 极客时间-小马哥《小马哥讲Spring核心编程思想》

你可能感兴趣的:(Spring系列,Spring,Bean,Bean,注解)