本章我们使用 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 的处理流程。
框架通过 AnnotationConfigUtils#registerAnnotationConfigProcessors() 方法主动去注册了这些处理的类
我们看 ConfigurationClassPostProcessor 中的相关方法
这里的 ConfigurationClass 在本示例中可以理解为 CircularReferencesDemo 类
解析 ConfigurationClass,并从类的信息中获取 @Bean 相关的方法信息
// 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 对象中。
我们通过 IDEA 查找下 BeanMethod 被哪些源码使用
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
相关代码如下:
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
从 configClass 获取相关的 BeanMethod 遍历循环,调用 loadBeanDefinitionsForBeanMethod 方法,看方法名称意思是通过 BeanMethod 加载 Bean 的定义。
那么我们将断点打到这个方法中,首先进来的是 student 方法
静态方法&实例方法
因为 @Bean 的注入方式是通过方法注入,所以需要区分静态方法和实例方法两种不同的场景
相关代码如下:
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 中。
@Bean 注解的处理流程总结为以下 4 个步骤: