购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 Spring依赖注入原理分析

Spring中关于依赖注入的代码实现非常丰富,涉及大量类和组件之间的协作与交互。从原理上讲,任何一个框架都存在一条核心执行流程,只要抓住这条主流程,我们就能把握框架的整体代码结构,Spring也不例外。无论采用何种依赖注入机制,前提都是Spring IoC容器正常启动。因此,IoC容器初始化就是我们理解和把握依赖注入实现机制的前提。

本节结合Bean的生命周期,把IoC容器初始化过程梳理成两大步骤,即Bean的注册和Bean的实例化。这两个步骤就构成了一条代码主流程。

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。

2.2.2 Bean的实例化

请注意,到现在为止,Spring IoC容器对Bean的创建过程并没有完成,我们只是将Bean的定义加载到了容器中而已。但是容器本身可能已经存在这些Bean的定义,所以我们还需要调用ApplicationContext接口的抽象实现类AbstractApplicationContext中的refresh()方法刷新容器,正如我们在前面看到的AnnotationConfigApplicationContext构造函数所执行的那样。可以说,refresh()方法是整个Spring容器中最为核心的一个方法,值得我们详细讨论。但因为这里关注的是依赖注入,所以我们只列出refresh()方法中与该主题相关的代码,如代码清单2-13所示。

代码清单2-13 AbstractApplicationContext中的refresh()方法代码

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 提取配置信息转化为BeanDefinition并注册到BeanFactory中
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        try {
            ...
            // 初始化所有的单例Bean
            finishBeanFactoryInitialization(beanFactory);
            ...
        }
    }
}

可以看到,obtainFreshBeanFactory()方法完成BeanDefinition的注册并返回一个Bean-Factory。对于AnnotationConfigApplicationContext而言,这一步实际上就是将BeanDefinition注册到DefaultListableBeanFactory而已,我们在前面已经介绍了这一步骤。

而finishBeanFactoryInitialization()方法才是真正的完成Bean实例化的入口。在这个方法中,完成Bean的实例化代码实际上位于它的子类DefaultListableBeanFactory中,如代码清单2-14所示。

代码清单2-14 DefaultListableBeanFactory中的preInstantiateSingletons()方法代码

@Override
public void preInstantiateSingletons() throws BeansException {
    List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

    // 触发所有非懒加载的单例Bean的初始化操作
    for (String beanName : beanNames) {
        ...
        //获取Bean
        getBean(beanName);
        ...
    }
}

接下来,我们就进入到getBean()方法了,这个方法可以从BeanFactory中获取一个Bean,而Bean的初始化过程也被封装在这个方法中。在getBean()方法中,我们一路跟踪代码会发现需要深入分析的实际上是如代码清单2-15所示的一个createBean()抽象方法。

代码清单2-15 BeanFactory中的createBean()方法代码

protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException;

请注意,在Spring中,实现这个抽象方法的唯一BeanFactory是AbstractAutowireCap-ableBeanFactory。从命名上看,我们就可以联想到@Autowired注解。在AbstractAutowire-CapableBeanFactory中,真正完成Bean的创建是在doCreateBean()方法中。doCreateBean()方法比较长,为了显示得更简洁,我们对代码做了大量裁剪之后得到如代码清单2-16所示的结构。

代码清单2-16 AbstractAutowireCapableBeanFactory中的doCreateBean()方法代码结构

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
    //1. 初始化Bean
    instanceWrapper = createBeanInstance(beanName, mbd, args);

    //2. 初始化Bean实例
    populateBean(beanName, mbd, instanceWrapper);

    //3. 执行初始化Bean实例的回调
    exposedObject = initializeBean(beanName, exposedObject, mbd);

    return exposedObject;
}

可以看到这里包含三个核心子方法,它们的名称和作用如图2-2所示。

045-1

图2-2 单例对象的初始化步骤示意图

在以上三个步骤中,createBeanInstance()方法用于根据配置生成具体的Bean,最终通过基于构造器的反射方法实现这一目标。请注意,执行完这一步之后,Bean已经被创建了,但还不完整,因为属性还没有被注入。

接下来的populateBean()方法就是用于实现属性的自动注入,包含byName、byType类型的自动装配,以及基于@Autowired、@Value注解的属性设值。执行完这一步之后,可以说Bean已经是完整的了。

而最后的initializeBean()方法则更多是一种扩展性的实现机制,用于在Bean初始化完成之后执行一些定制化操作。

至此,针对整个Bean的注入过程(即Bean的注册和实例化),我们围绕核心流程做了剖析和总结。在这个过程中,比较容易碰到的一个问题就是陷入代码的细节而忽略了主体步骤。因此,如果想要跳出源码阅读的困境,快速掌握框架的实现原理,我们就必须从核心流程来看待框架。

接下来,我们将对Spring依赖注入中典型的循环依赖问题进行系统分析。可以说,想要理解循环依赖问题以及对应的解决方案,掌握本节阐述的Bean的注册和实例化原理至关重要。 yBr+Q5/CPn/uxPX8ncr/7BF3I/W1PUyhVLTpJFQ+a4PIi2+0rUYiC3iCbM3qxkDU

点击中间区域
呼出菜单
上一章
目录
下一章
×