Spring Boot进阶:原理、实战与面试题分析
上QQ阅读APP看书,第一时间看更新

2.2.1 Bean的注册

在使用Spring时,我们可以通过获取一个应用上下文(ApplicationContext)对象来操作各种Bean,示例代码如代码清单2-9所示,相信你对这段代码不会陌生。

代码清单2-9 获取应用上下文对象的示例代码

AnnotationConfigApplicationContext applicationContext
    = new AnnotationConfigApplicationContext(AppConfig.class);

这里的ApplicationContext接口代表的就是一个Spring IoC容器,而在Spring中存在一大批ApplicationContext接口的实现类。如果使用基于注解的配置方式,就可以使用上述代码中的AnnotationConfigApplicationContext来初始化容器上下文对象。

在刚开始阅读Spring源码时,我建议你直接从AnnotationConfigApplicationContext的启动流程切入,这一流程位于它的构造函数中,如代码清单2-10所示。

代码清单2-10 AnnotationConfigApplicationContext构造函数代码

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    //根据注解配置类注册Bean
    register(annotatedClasses);
    //刷新容器
    refresh();
}

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    //根据包路径配置扫描Bean
    scan(basePackages);
    //刷新容器
    refresh();
}

这两个构造函数的作用很明确,一个是根据注解配置类注册Bean,另一个则是根据包路径配置扫描Bean。这里我们以register()方法为例,来讨论Bean的注册过程,该方法如代码清单2-11所示。

代码清单2-11 AnnotationConfigApplicationContext的register()方法代码

public void register(Class<?>... annotatedClasses) {
    Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
    this.reader.register(annotatedClasses);
}

这里依赖AnnotatedBeanDefinitionReader工具类来完成Bean的注册。AnnotatedBean-DefinitionReader会遍历所有传入的annotatedClasses注解类,然后通过代码清单2-12所示的doRegisterBean()方法完成注册。(由于该方法的代码较长,我们对重要逻辑添加了注释,对不重要的代码做了省略。)

代码清单2-12 AnnotationConfigApplicationContext的doRegisterBean()方法代码

<T> void doRegisterBean(...) {
    //将注解配置类信息转换成一种BeanDefinition
    AnnotatedGenericBeanDefinition abd = new
    AnnotatedGenericBeanDefinition(annotatedClass);
    …
    //获取Bean的作用域元数据,解析Bean作用域
    ScopeMetadata scopeMetadata =
    this.scopeMetadataResolver.resolveScopeMetadata(abd);

    //将Bean的作用域写回BeanDefinition
    abd.setScope(scopeMetadata.getScopeName());

    //生成beanName
    String beanName = (name != null ? name :
    this.beanNameGenerator.generateBeanName(abd, this.registry));

    //解析AnnotatedBeanDefinitionReader中的@Lazy、@Primary等注解
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

    //处理@Qualifier注解
    if (qualifiers != null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            if (Primary.class == qualifier) {
                // 如果设置了@Primary注解,则设置当前Bean为首选Bean
                abd.setPrimary(true);
            }
            else if (Lazy.class == qualifier) {
                //如果设置了@Lazy注解,则设置当前Bean为延迟加载模式
                abd.setLazyInit(true);
            }
            else {
                //其他注解,则添加到BeanDefinition中
                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
            }
        }
    }
    …
    //注册Bean对象
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

这段代码包含Bean注册过程中的三个核心步骤,如图2-1所示。

043-1

图2-1 AnnotatedBeanDefinitionReader中Bean注册的核心步骤示意图

首先,我们构建用来描述Bean实例信息的BeanDefinition对象,这需要将注解配置类信息转成AnnotatedGenericBeanDefinition数据结构,而AnnotatedGenericBeanDefinition就是一种BeanDefinition,包含了Bean的构造函数参数、各种属性值以及所添加的注解信息。

然后,我们设置BeanDefinition属性,这一步骤完成了对@Scope、@Primary、@Lazy等注解的处理。

最后,通过registerBeanDefinition()方法完成Bean的注册,该方法内部通过Listable-BeanFactory的实现类DefaultListableBeanFactory将Bean定义信息注册到Spring IoC容器中。ListableBeanFactory是Spring中常用的一个BeanFactory,通过这个接口,我们可以一次获取多个Bean。