(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解

目录

一. 前言

二. Spring IoC 的体系结构设计

2.1. BeanFactory 和 BeanRegistry:IoC 容器功能规范和 Bean 的注册

2.1.1. BeanFactory 定义了 IoC 容器基本功能规范?

2.1.2. BeanFactory 为何要定义这么多层次的接口?定义了哪些接口?

2.1.3. 如何将 Bean 注册到 BeanFactory 中?

2.2. BeanDefinition:各种 Bean 对象及其相互的关系

2.2.1. BeanDefinition

2.2.2. BeanDefinitionReader

2.2.3. BeanDefinitionHolder

2.3. ApplicationContext:IoC 接口设计和实现

2.3.1. ApplicationContext 接口的设计

2.3.2. ApplicationContext 接口的实现

三. Spring Bean 初始化流程

3.1. 初始化的入口

3.2. 设置资源解析器和环境

3.3. 设置配置路径

3.4. 初始化的主体流程

3.5. 初始化 BeanFactory 之 obtainFreshBeanFactory

3.6. 初始化 BeanFactory 之 loadBeanDefinitions

3.7. AbstractBeanDefinitionReader 读取 Bean 定义资源

3.8. XmlBeanDefinitionReader 加载 Bean 定义资源

3.9. DocumentLoader 将 Bean 定义资源转换为 Document 对象

3.10. XmlBeanDefinitionReader 解析载入的 Bean 定义资源文件

3.11. DefaultBeanDefinitionDocumentReader 对 Bean 定义的 Document 对象解析

3.12. BeanDefinitionParserDelegate 解析 Bean 定义资源文件生成 BeanDefinition

3.13. 解析过后的 BeanDefinition 在 IoC 容器中的注册

3.14. DefaultListableBeanFactory 向 IoC 容器注册解析后的 BeanDefinition

3.15. 小结

四. 实例化 Bean 对象

4.1. BeanFactory 中 getBean 的主体思路

4.1.1. 初步思考

4.1.2. Spring 中 getBean 的主体思路

4.2. Spring 如何解决循环依赖问题

4.3. Spring 中 Bean 的生命周期


一. 前言

    在设计时,首先需要考虑的是 IoC 容器的功能(输入和输出),承接《(一)Spring 核心之控制反转(IoC)—— 配置及使用》,我们初步的画出 IoC 容器的整体功能。

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第1张图片

在此基础上,我们初步的去思考,如果作为一个 IoC 容器的设计者,主体上应该包含哪几个部分:

  1. 加载 Bean 的配置(比如 xml 配置):比如不同类型资源的加载,解析成生成统一 Bean 的定义。
  2. 根据 Bean 的定义加载生成 Bean 的实例,并放置在 Bean 容器中:比如 Bean 的依赖注入,Bean 的嵌套,Bean 存放(缓存)等。
  3. 除了基础 Bean 外,还有常规针对企业级业务的特别 Bean:比如国际化 Message,事件Event 等生成特殊的类结构去支撑。
  4. 对容器中的 Bean 提供统一的管理和调用:比如用工厂模式管理,提供方法根据名字/类的类型等从容器中获取 Bean。

这种思考的过程才是建设性的,知识体系的构建才是高效的。

二. Spring IoC 的体系结构设计

2.1. BeanFactory 和 BeanRegistry:IoC 容器功能规范和 Bean 的注册

    Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,也即 IoC 容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在 Spring 中有许多的 IoC 容器的实现供用户选择和使用,这是 IoC 容器的基础。

在顶层的结构设计主要围绕着 BeanFactory 和 xxxRegistry 进行:

  • BeanFactory:工厂模式定义了 IoC 容器的基本功能规范。
  • BeanRegistry:向 IoC 容器手工注册 BeanDefinition 对象的方法。

其相互关系如下:

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第2张图片

2.1.1. BeanFactory 定义了 IoC 容器基本功能规范?

    BeanFactory 作为最顶层的一个接口类,它定义了 IoC 容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。我们看下 BeanFactory 接口:

public interface BeanFactory {
    // 用于取消引用实例并将其与FactoryBean创建的bean区分开来。例如,如果命名的bean是FactoryBean,则获取将返回Factory,而不是Factory返回的实例。
    String FACTORY_BEAN_PREFIX = "&"; 
        
    // 根据bean的名字和Class类型等来得到bean实例    
    Object getBean(String name) throws BeansException;    
    Object getBean(String name, Class requiredType) throws BeansException;    
    Object getBean(String name, Object... args) throws BeansException;
     T getBean(Class requiredType) throws BeansException;
     T getBean(Class requiredType, Object... args) throws BeansException;

    // 返回指定bean的Provider
     ObjectProvider getBeanProvider(Class requiredType);
     ObjectProvider getBeanProvider(ResolvableType requiredType);

    // 检查工厂中是否包含给定name的bean,或者外部注册的bean
    boolean containsBean(String name);

    // 检查所给定name的bean是否为单例/原型
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    // 判断所给name的类型与type是否匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException;

    // 获取给定name的bean的类型
    @Nullable
    Class getType(String name) throws NoSuchBeanDefinitionException;

    // 返回给定name的bean的别名
    String[] getAliases(String name);    
}

2.1.2. BeanFactory 为何要定义这么多层次的接口?定义了哪些接口?

    主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制

接口如下:

  • ListableBeanFactory:该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 Bean 的个数、获取某一类型 Bean 的配置名、查看容器中是否包括某一 Bean 等方法;
  • HierarchicalBeanFactory:父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器; 通过 HierarchicalBeanFactory 接口, Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。Spring 使用父子容器实现了很多功能,比如在 Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。
  • ConfigurableBeanFactory:是一个重要的接口,增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法;
  • ConfigurableListableBeanFactory:ListableBeanFactory 和 ConfigurableBeanFactory 的融合;
  • AutowireCapableBeanFactory:定义了将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法。

2.1.3. 如何将 Bean 注册到 BeanFactory 中?

    Spring 配置文件中每一个 节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册 BeanDefinition 对象的方法。

2.2. BeanDefinition:各种 Bean 对象及其相互的关系

    Bean 对象存在依赖嵌套等关系,所以设计者设计了 BeanDefinition,它用来对 Bean 对象及关系定义。我们在理解时只需要抓住如下三个要点:

  1. BeanDefinition 定义了各种 Bean 对象及其相互的关系。
  2. BeanDefinitionReader 这是 BeanDefinition 的解析器。
  3. BeanDefinitionHolder 这是 BeanDefination 的包装类,用来存储 BeanDefinition,name 以及 aliases 等。

2.2.1. BeanDefinition

    Spring IoC 容器管理了我们定义的各种 Bean 对象及其相互的关系,Bean 对象在 Spring 实现中是以 BeanDefinition 来描述的,其继承体系如下:

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第3张图片

2.2.2. BeanDefinitionReader

    Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第4张图片

2.2.3. BeanDefinitionHolder

    BeanDefinitionHolder 这是 BeanDefination 的包装类,用来存储 BeanDefinition,name 以及aliases 等。

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第5张图片

2.3. ApplicationContext:IoC 接口设计和实现

    Spring IoC 容器的接口类是 ApplicationContext,很显然它必然继承 BeanFactory 对 Bean 规范(最基本的 IoC 容器的实现)进行定义。而 ApplicationContext 表示的是应用的上下文,除了对Bean 的管理外,还至少应该包含了:

  1. 访问资源:对不同方式的 Bean 配置(即资源)进行加载。(实现 ResourcePatternResolver接口)。
  2. 国际化:支持信息源,可以实现国际化。(实现 MessageSource 接口)。
  3. 应用事件:支持应用事件。(实现 ApplicationEventPublisher 接口)。

2.3.1. ApplicationContext 接口的设计

我们来看下 ApplicationContext 整体结构:

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第6张图片

  • HierarchicalBeanFactory 和 ListableBeanFactory:ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础上,还通过多个其他的接口扩展了 BeanFactory 的功能。
  • ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。实现了 ApplicationListener 事件监听接口的 Bean 可以接收到容器事件,并对事件进行响应处理。在 ApplicationContext 抽象实现类 AbstractApplicationContext 中,我们可以发现存在一个 ApplicationEventMulticaster,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听者。
  • MessageSource:为应用提供 i18n 国际化消息访问的功能。
  • ResourcePatternResolver:所有 ApplicationContext 实现类都实现了类似于PathMatchingResourcePatternResolver 的功能,可以通过带前缀的 Ant 风格的资源文件路径装载 Spring 的配置文件。
  • LifeCycle:该接口是 Spring 2.0 加入的,该接口提供了 start() 和 stop() 两个方法,主要用于控制异步处理过程。在具体使用时,该接口同时被 ApplicationContext 实现及具体 Bean 实现, ApplicationContext 会将 start/stop 的信息传递给容器中所有实现了该接口的 Bean,以达到管理和控制 JMX、任务调度等目的。

2.3.2. ApplicationContext 接口的实现

    在考虑 ApplicationContext 接口的实现时,关键的点在于,不同 Bean 的配置方式(比如xml、groovy、annotation 等)有着不同的资源加载方式,这便衍生出了众多 ApplicationContext 的实现类。

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第7张图片

第一,从类结构设计上看, 围绕着是否需要 Refresh 容器衍生出两个抽象类:

  • GenericApplicationContext:是初始化的时候就创建容器,往后的每次 refresh 都不会更改。
  • AbstractRefreshableApplicationContext:AbstractRefreshableApplicationContext 及子类,每次 refresh 都是先清除已有(如果不存在就创建)的容器,然后再重新创建;AbstractRefreshableApplicationContext 及子类无法做到 GenericApplicationContext 混合搭配从不同源头获取 Bean 的定义信息。

第二,从加载的源来看(比如 xml、groovy、annotation 等),衍生出众多类型的ApplicationContext,典型比如:

  • FileSystemXmlApplicationContext:从文件系统下的一个或多个 xml 配置文件中加载上下文定义,也就是说系统盘符中加载 xml 配置文件。
  • ClassPathXmlApplicationContext:从类路径下的一个或多个 xml 配置文件中加载上下文定义,适用于 xml 配置的方式。
  • AnnotationConfigApplicationContext:从一个或多个基于 Java 的配置类中加载上下文定义,适用于 Java 注解的方式。
  • ConfigurableApplicationContext:扩展于 ApplicationContext,它新增加了两个主要的方法:refresh() 和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh() 即可启动应用上下文,在已经启动的状态下,调用 refresh() 则清除缓存并重新装载配置信息,而调用 close() 则可关闭应用上下文。这些接口方法为容器的控制管理带来了便利,但作为开发者,我们并不需要过多关心这些方法。

    设计者在设计时 AnnotationConfigApplicationContext 为什么是继承GenericApplicationContext?因为基于注解的配置,是不太会被运行时修改的,这意味着不需要进行动态 Bean 配置和刷新容器,所以只需要 GenericApplicationContext。

    而基于 XML 这种配置文件,这种文件是容易修改的,需要动态性刷新 Bean 的支持,所以 XML相关的配置必然继承 AbstractRefreshableApplicationContext;且存在多种 xml 的加载方式(位置不同的设计),所以必然会设计出 AbstractXmlApplicationContext,其中包含对 XML 配置解析成BeanDefination 的过程。

    那么细心的你从上图可以发现 AnnotationWebConfigApplicationContext 确实继承了AbstractRefreshableApplicationContext 而不是 GenericApplicationContext,为什么AnnotationWebConfigApplicationContext 继承自 AbstractRefreshableApplicationContext 呢?因为用户可以通过 ApplicationContextInitializer 来设置 contextInitializerClasses(context-param / init-param),在这种情况下用户是倾向于刷新 Bean 的,所以设计者选择让AnnotationWebConfigApplicationContext 继承了 AbstractRefreshableApplicationContext。

如下是源码中 Spring 设计者对它的解释:

 * 

As an alternative to setting the "contextConfigLocation" parameter, users may * implement an {@link org.springframework.context.ApplicationContextInitializer * ApplicationContextInitializer} and set the * {@linkplain ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM "contextInitializerClasses"} * context-param / init-param. In such cases, users should favor the {@link #refresh()} * and {@link #scan(String...)} methods over the {@link #setConfigLocation(String)} * method, which is primarily for use by {@code ContextLoader}.

我们把之前的设计要点和设计结构结合起来看:

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第8张图片

    到此,基本可以帮助你从顶层构建对 IoC 容器的设计理解,而不是过早沉溺于代码的细节。所以本文最大的目标是帮助你构筑体系化的认知,如果你自己去看源码而不站在顶层设计角度出发, 你多半会捡了芝麻丢了西瓜,时间一长啥印象没有。

三. Spring Bean 初始化流程

    Spring 是如何将 Bean 从 XML 配置中解析后放到 IoC 容器中的?本节的目标就是分析 Spring 如何实现将资源配置(以 xml 配置为例)通过加载、解析、生成 BeanDefination 并注册到 IoC 容器中的。

3.1. 初始化的入口

    对于 xml 配置的 Spring 应用,在 main() 方法中实例化 ClasspathXmlApplicationContext 即可创建一个 IoC 容器。我们可以从这个构造方法开始,探究一下 IoC 容器的初始化过程。

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "daos.xml", "services.xml");
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
    this(configLocations, true, (ApplicationContext)null);
}

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    // 设置Bean资源加载器
    super(parent);

    // 设置配置路径
    this.setConfigLocations(configLocations);

    // 初始化容器
    if (refresh) {
        this.refresh();
    }
}

3.2. 设置资源解析器和环境

    调用父类容器 AbstractApplicationContext 的构造方法(super(parent) 方法)为容器设置好Bean 资源加载器。

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
    // 默认构造函数初始化容器id, name, 状态 以及 资源解析器
    this();

    // 将父容器的Environment合并到当前容器
    this.setParent(parent);
}

通过 AbstractApplicationContext 默认构造函数初始化容器 id、name、状态以及资源解析器。

public AbstractApplicationContext() {
    this.logger = LogFactory.getLog(this.getClass());
    this.id = ObjectUtils.identityToString(this);
    this.displayName = ObjectUtils.identityToString(this);
    this.beanFactoryPostProcessors = new ArrayList();
    this.active = new AtomicBoolean();
    this.closed = new AtomicBoolean();
    this.startupShutdownMonitor = new Object();
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.applicationListeners = new LinkedHashSet();
    this.resourcePatternResolver = this.getResourcePatternResolver();
}

// Spring资源加载器
protected ResourcePatternResolver getResourcePatternResolver() {
    return new PathMatchingResourcePatternResolver(this);
}

通过 AbstractApplicationContext 的 setParent(parent) 方法将父容器的 Environment 合并到当前容器。

public void setParent(@Nullable ApplicationContext parent) {
    this.parent = parent;
    if (parent != null) {
        Environment parentEnvironment = parent.getEnvironment();
        if (parentEnvironment instanceof ConfigurableEnvironment) {
            this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
        }
    }
}

3.3. 设置配置路径

    在设置容器的资源加载器之后,接下来 FileSystemXmlApplicationContet 执行setConfigLocations 方法通过调用其父类 AbstractRefreshableConfigApplicationContext 的方法进行对 Bean 定义资源文件的定位。

public void setConfigLocations(@Nullable String... locations) {
    if (locations != null) {
        Assert.noNullElements(locations, "Config locations must not be null");
        this.configLocations = new String[locations.length];

        for(int i = 0; i < locations.length; ++i) {
            // 解析配置路径
            this.configLocations[i] = this.resolvePath(locations[i]).trim();
        }
    } else {
        this.configLocations = null;
    }
}

protected String resolvePath(String path) {
    // 从上一步Environment中解析
    return this.getEnvironment().resolveRequiredPlaceholders(path);
}

3.4. 初始化的主体流程

    Spring IoC 容器对 Bean 定义资源的载入是从 refresh() 函数开始的,refresh() 是一个模板方法,refresh() 方法的作用是:在创建 IoC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IoC 容器。refresh 的作用类似于对 IoC 容器的重启,在新建立好的容器中对容器进行初始化,对 Bean 定义资源进行载入。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

    这里的设计上是一个非常典型的资源类加载处理型的思路,头脑中需要形成如下图的顶层思路(而不是只停留在流水式的方法上面):

  1. 模板方法设计模式,模板方法中使用典型的钩子方法
  2. 将具体的初始化加载方法插入到钩子方法之间;
  3. 将初始化的阶段封装,用来记录当前初始化到什么阶段;常见的设计是xxxPhase/xxxStage;
  4. 资源加载初始化有失败等处理,必然是 try/catch/finally...

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第9张图片

3.5. 初始化 BeanFactory 之 obtainFreshBeanFactory

    AbstractApplicationContext 的 obtainFreshBeanFactory() 方法调用子类容器的refreshBeanFactory() 方法,启动容器载入 Bean 定义资源文件的过程,代码如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
    refreshBeanFactory();
    return getBeanFactory();
}

    AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory() 方法,容器真正调用的是其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory() 方法。在创建 IoC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IoC 容器。方法的源码如下:

protected final void refreshBeanFactory() throws BeansException {
    // 如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory); // 对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等 
        loadBeanDefinitions(beanFactory); // 调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器  
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

3.6. 初始化 BeanFactory 之 loadBeanDefinitions

    AbstractRefreshableApplicationContext 中只定义了抽象的 loadBeanDefinitions 方法,容器真正调用的是其子类 AbstractXmlApplicationContext 对该方法的实现,AbstractXmlApplicationContext 的主要源码如下:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源  
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 配置上下文的环境,资源加载器、解析器
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 为Bean读取器设置SAX xml解析器

    // 允许子类自行初始化(比如校验机制),并提供真正的加载方法
    initBeanDefinitionReader(beanDefinitionReader); // 当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制  
    loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    // 加载XML配置方式里的Bean定义的资源
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    // 加载构造函数里配置的Bean配置文件,即{"aspects.xml", "daos.xml", "services.xml"}
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        reader.loadBeanDefinitions(configLocations);
    }
}

    Xml Bean读取器(XmlBeanDefinitionReader)调用其父类 AbstractBeanDefinitionReader 的 reader.loadBeanDefinitions 方法读取 Bean 定义资源。

    由于我们使用 ClassPathXmlApplicationContext 作为例子分析,因此 getConfigResources 的返回值为 null,因此程序执行 reader.loadBeanDefinitions(configLocations) 分支。

3.7. AbstractBeanDefinitionReader 读取 Bean 定义资源

AbstractBeanDefinitionReader 的 loadBeanDefinitions方法源码如下:

@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
}

public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException {
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
                "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    }

    // 模式匹配类型的解析器,这种方式是加载多个满足匹配条件的资源
    if (resourceLoader instanceof ResourcePatternResolver) {
        try {
            // 获取到要加载的资源
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            int count = loadBeanDefinitions(resources); // 委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能  
            if (actualResources != null) {
                Collections.addAll(actualResources, resources);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
            }
            return count;
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "Could not resolve bean definition resource pattern [" + location + "]", ex);
        }
    }
    else {
        // 只能通过绝对路径URL加载单个资源.
        Resource resource = resourceLoader.getResource(location);
        int count = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
        }
        return count;
    }
}

从对 AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源码分析可以看出该方法做了以下两件事:

  1. 首先,调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源。
  2. 其次,真正执行加载功能是其子类 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。

3.8. XmlBeanDefinitionReader 加载 Bean 定义资源

    继续看子类 XmlBeanDefinitionReader 的 loadBeanDefinitions(Resource …) 方法看到代表 Bean 文件的资源定义以后的载入过程。

/**
 * 本质上是加载XML配置的Bean。
 * @param inputSource the SAX InputSource to read from
 * @param resource the resource descriptor for the XML file
 */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {

    try {
        Document doc = doLoadDocument(inputSource, resource); // 将Bean定义资源转换成Document对象
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    }
    catch (SAXParseException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    }
    catch (SAXException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                "XML document from " + resource + " is invalid", ex);
    }
    catch (ParserConfigurationException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Parser configuration exception parsing XML from " + resource, ex);
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "IOException parsing XML document from " + resource, ex);
    }
    catch (Throwable ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Unexpected exception parsing XML document from " + resource, ex);
    }
}

// 使用配置的DocumentLoader加载XML定义文件为Document.
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
            getValidationModeForResource(resource), isNamespaceAware());
}

    通过源码分析,载入 Bean 定义资源文件的最后一步是将 Bean 定义资源转换为 Document 对象,该过程由 documentLoader 实现。

3.9. DocumentLoader 将 Bean 定义资源转换为 Document 对象

DocumentLoader 将 Bean 定义资源转换成 Document 对象的源码如下:

// 使用标准的JAXP将载入的Bean定义资源转换成document对象
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

    // 创建文件解析器工厂
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isTraceEnabled()) {
        logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    // 创建文档解析器
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource); // 解析
}

protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
        throws ParserConfigurationException {

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(namespaceAware);

    // 设置解析XML的校验
    if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
        factory.setValidating(true);
        if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
            // Enforce namespace aware for XSD...
            factory.setNamespaceAware(true);
            try {
                factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
            }
            catch (IllegalArgumentException ex) {
                ParserConfigurationException pcex = new ParserConfigurationException(
                        "Unable to validate using XSD: Your JAXP provider [" + factory +
                        "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                        "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                pcex.initCause(ex);
                throw pcex;
            }
        }
    }

    return factory;
}

该解析过程调用 JavaEE 标准的 JAXP 标准进行处理。

至此 Spring IoC 容器根据定位的 Bean 定义资源文件,将其加载读入并转换成为 Document 对象过程完成。

接下来我们要继续分析 Spring IoC 容器将载入的 Bean 定义资源文件转换为 Document 对象之后,是如何将其解析为 Spring IoC 管理的 Bean 对象并将其注册到容器中的。

3.10. XmlBeanDefinitionReader 解析载入的 Bean 定义资源文件

    XmlBeanDefinitionReader 类中的 doLoadBeanDefinitions 方法是从特定 XML 文件中实际载入Bean 定义资源的方法,该方法在载入 Bean 定义资源之后将其转换为 Document 对象,接下来调用 registerBeanDefinitions 启动 Spring IoC 容器对 Bean定义 的解析过程,registerBeanDefinitions 方法源码如下:

// 按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构 
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 解析过程入口,这里使用了委派模式,具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成  
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;  // 返回此次解析了多少个对象
}

// 创建BeanDefinitionDocumentReader对象,解析Document对象  
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    return BeanUtils.instantiateClass(this.documentReaderClass);
}

/**
 * Create the {@link XmlReaderContext} to pass over to the document reader.
 */
public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
            this.sourceExtractor, this, getNamespaceHandlerResolver());
}

Bean 定义资源的载入解析分为以下两个过程:

  1. 首先,通过调用 XML 解析器将 Bean 定义资源文件转换得到 Document 对象,但是这些Document 对象并没有按照 Spring 的 Bean 规则进行解析。这一步是载入的过程。
  2. 其次,在完成通用的 XML 解析之后,按照 Spring 的 Bean 规则对 Document 对象进行解析。

    按照 Spring 的 Bean 规则对 Document 对象解析的过程是在接口 BeanDefinitionDocumentReader的实现类 DefaultBeanDefinitionDocumentReader 中实现的。

3.11. DefaultBeanDefinitionDocumentReader 对 Bean 定义的 Document 对象解析

    BeanDefinitionDocumentReader 接口通过 registerBeanDefinitions 方法调用其实现类DefaultBeanDefinitionDocumentReader 对 Document 对象进行解析,解析的代码如下:

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
}

// 注册配置的Beans
@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
    // Any nested  elements will cause recursion in this method. In
    // order to propagate and preserve  default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // We cannot use Profiles.of(...) since profile expressions are not supported
            // in XML config. See SPR-12458 for details.
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }

    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate); // 从Document的根元素开始进行Bean定义的Document对象  
    postProcessXml(root);

    this.delegate = parent;
}

3.12. BeanDefinitionParserDelegate 解析 Bean 定义资源文件生成 BeanDefinition

/**
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @param root the DOM root element of the document
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
      
    // 如果元素节点是导入元素,进行导入解析
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    // 如果元素节点是别名元素,进行别名解析 
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    // 如果元素节点元素, 按照Spring的Bean规则解析元素  
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    // 如果元素节点元素,即它是嵌套类型的
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // 递归解析
        doRegisterBeanDefinitions(ele);
    }
}

解析 Bean 生成 BeanDefinitionHolder 的方法:

/**
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 注册最终的装饰实例
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" +
                    bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event.
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

这里主要就是解析 XML 各种元素,来生成 BeanDefinition。

3.13. 解析过后的 BeanDefinition 在 IoC 容器中的注册

    Document 对象的解析后得到封装 BeanDefinition 的 BeanDefinitionHold 对象,然后调用BeanDefinitionReaderUtils 的 registerBeanDefinition 方法向 IoC 容器注册解析的 Bean,BeanDefinitionReaderUtils 的注册的源码如下:

// 通过BeanDefinitionRegistry将BeanDefinitionHolder注册到BeanFactory
public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {

    // Register bean definition under primary name.
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // Register aliases for bean name, if any.
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

    当调用 BeanDefinitionReaderUtils 向 IoC 容器注册解析的 BeanDefinition 时,真正完成注册功能的是 DefaultListableBeanFactory。

3.14. DefaultListableBeanFactory 向 IoC 容器注册解析后的 BeanDefinition

IoC 容器本质上就是一个 beanDefinitionMap,注册即将 BeanDefinition put 到 map 中。

/** Map of bean definition objects, keyed by bean name. */
private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);

/** Map from bean name to merged BeanDefinitionHolder. */
private final Map mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);


@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    // 如果已经注册
    if (existingDefinition != null) {
        // 检查是否可以覆盖
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
            if (logger.isInfoEnabled()) {
                logger.info("Overriding user-defined bean definition for bean '" + beanName +
                        "' with a framework-generated bean definition: replacing [" +
                        existingDefinition + "] with [" + beanDefinition + "]");
            }
        }
        else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                        "' with a different definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Overriding bean definition for bean '" + beanName +
                        "' with an equivalent definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        // 覆盖
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                removeManualSingletonName(beanName);
            }
        }
        else {
            // Still in startup registration phase
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        //重置所有已经注册过的BeanDefinition的缓存  
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
    else if (isConfigurationFrozen()) {
        clearByTypeCache();
    }
}

    至此,Bean 定义资源文件中配置的 Bean 被解析过后,已经注册到 IoC 容器中,被容器管理起来,真正完成了 IoC 容器初始化所做的全部工作。现在 IoC 容器中已经建立了整个 Bean 的配置信息,这些 BeanDefinition 信息已经可以使用,并且可以被检索,IoC 容器的作用就是对这些注册的Bean 定义信息进行处理和维护。这些注册的 Bean 定义信息是 IoC 容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。

3.15. 小结

通过上面的代码,总结一下 IoC 容器初始化的基本步骤:

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第10张图片

1. 初始化的入口在容器实现中的 refresh() 调用来完成。

2. 对 Bean 定义载入 IoC 容器使用的方法是 loadBeanDefinition,其中的大致过程如下:

  1. 通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径、文件系统、URL 等方式来定为资源位置。如果是 XmlBeanFactory 作为 IoC 容器,那么需要为它指定 Bean 定义的资源,也就是说 Bean 定义文件是通过抽象成 Resource 来被 IoC 容器处理的
  2. 通过 BeanDefinitionReader 来完成定义信息的解析和 Bean 信息的注册,往往使用的是XmlBeanDefinitionReader 来解析 Bean 的 xml 定义文件 -- 实际的处理过程是委托给 BeanDefinitionParserDelegate 来完成的,从而得到 Bean 的定义信息,这些信息在 Spring 中使用 BeanDefinition 对象来表示 -- 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition 这些相关的方法 -- 他们都是为处理 BeanDefinitin 服务的。
  3. 容器解析得到 BeanDefinition 以后,需要把它在 IoC 容器中注册,这由 IoC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IoC 容器内部维护的一个 HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IoC 容器持有 Bean 信息的场所,以后对 Bean 的操作都是围绕这个HashMap 来实现的。

3. 然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IoC 的服务了,在使用 IoC 容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IoC 风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。Spring 本身提供了对声明式载入 Web 应用程序用法的应用程序上下文,并将其存储在 ServletContext 中的框架实现。

四. 实例化 Bean 对象

    本节主要研究如何从 IoC 容器已有的 BeanDefinition 信息,实例化出 Bean 对象。这里还会包括三块重点内容:

  1. BeanFactory 中 getBean 的主体思路。
  2. Spring 如何解决循环依赖问题。
  3. Spring 中 Bean 的生命周期。

4.1. BeanFactory 中 getBean 的主体思路

    我们知道 BeanFactory 定义了 Bean 容器的规范,其中包含根据 Bean 的名字,Class 类型和参数等来得到 Bean 实例。

// 根据bean的名字和Class类型等来得到bean实例    
Object getBean(String name) throws BeansException;    
Object getBean(String name, Class requiredType) throws BeansException;    
Object getBean(String name, Object... args) throws BeansException;
 T getBean(Class requiredType) throws BeansException;
 T getBean(Class requiredType, Object... args) throws BeansException;

4.1.1. 初步思考

    上节我们已经分析了 IoC 初始化的流程,最终是将 Bean 的定义即 BeanDefinition 放到beanDefinitionMap 中,本质上是一个 ConcurrentHashMap,并且 BeanDefinition 接口中包含了这个类的 Class 信息以及是否是单例等。

(二)Spring 核心之控制反转(IoC)—— 体系结构设计及原理详解_第11张图片

这样我们初步有了实现 Object getBean(String name) 这个方法的思路:

  1. 从 beanDefinitionMap 通过 beanName 获得 BeanDefinition;
  2. 从 BeanDefinition 中获得 beanClassName;
  3. 通过反射初始化 beanClassName 的实例 instance:
    1. 构造函数从 BeanDefinition 的 getConstructorArgumentValues() 方法获取。
    2. 属性值从 BeanDefinition 的 getPropertyValues() 方法获取。
  4. 返回 beanName 的实例 instance。

    由于 BeanDefinition 还有单例的信息,如果是无参构造函数的实例还可以放在一个缓存中,这样下次获取这个单例的实例时只需要从缓存中获取,如果获取不到再通过上述步骤获取。

PS:如上只是我们初步的思路,而 Spring 还需要考虑各种设计上的问题,比如 beanDefinition 中其它定义,循环依赖等,所以我们来看下 Spring 是如何是如何实现的。

4.1.2. Spring 中 getBean 的主体思路

    BeanFactory 实现 getBean 方法在 AbstractBeanFactory 中,这个方法重载都是调用doGetBean 方法进行实现的:

public Object getBean(String name) throws BeansException {
  return doGetBean(name, null, null, false);
}
public  T getBean(String name, Class requiredType) throws BeansException {
  return doGetBean(name, requiredType, null, false);
}
public Object getBean(String name, Object... args) throws BeansException {
  return doGetBean(name, null, args, false);
}
public  T getBean(String name, @Nullable Class requiredType, @Nullable Object... args)
    throws BeansException {
  return doGetBean(name, requiredType, args, false);
}

我们来看下 doGetBean 方法(这个方法很长,我们主要看它的整体思路和设计要点):

// 参数typeCheckOnly:bean实例是否包含一个类型检查
protected  T doGetBean(
			String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

  // 解析bean的真正name,如果bean是工厂类,name前缀会加&,需要去掉
  String beanName = transformedBeanName(name);
  Object beanInstance;

  // Eagerly check singleton cache for manually registered singletons.
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    // 无参单例从缓存中获取
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }

  else {
    // 如果bean实例还在创建中,则直接抛出异常
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }

    // 如果 bean definition 存在于父的bean工厂中,委派给父Bean工厂获取
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      // Not found -> check parent.
      String nameToLookup = originalBeanName(name);
      if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
            nameToLookup, requiredType, args, typeCheckOnly);
      }
      else if (args != null) {
        // Delegation to parent with explicit args.
        return (T) parentBeanFactory.getBean(nameToLookup, args);
      }
      else if (requiredType != null) {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
      }
      else {
        return (T) parentBeanFactory.getBean(nameToLookup);
      }
    }

    if (!typeCheckOnly) {
      // 将当前bean实例放入alreadyCreated集合里,标识这个bean准备创建了
      markBeanAsCreated(beanName);
    }

    StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
        .tag("beanName", name);
    try {
      if (requiredType != null) {
        beanCreation.tag("beanType", requiredType::toString);
      }
      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);

      // 确保它的依赖也被初始化了.
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (String dep : dependsOn) {
          if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
          }
          registerDependentBean(dep, beanName);
          try {
            getBean(dep); // 初始化它依赖的Bean
          }
          catch (NoSuchBeanDefinitionException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
          }
        }
      }

      // 创建Bean实例:单例
      if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
          try {
            // 真正创建bean的方法
            return createBean(beanName, mbd, args);
          }
          catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
          }
        });
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
      // 创建Bean实例:原型
      else if (mbd.isPrototype()) {
        // It's a prototype -> create a new instance.
        Object prototypeInstance = null;
        try {
          beforePrototypeCreation(beanName);
          prototypeInstance = createBean(beanName, mbd, args);
        }
        finally {
          afterPrototypeCreation(beanName);
        }
        beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
      }
      // 创建Bean实例:根据bean的scope创建
      else {
        String scopeName = mbd.getScope();
        if (!StringUtils.hasLength(scopeName)) {
          throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
        }
        Scope scope = this.scopes.get(scopeName);
        if (scope == null) {
          throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
        }
        try {
          Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
              return createBean(beanName, mbd, args);
            }
            finally {
              afterPrototypeCreation(beanName);
            }
          });
          beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
        }
        catch (IllegalStateException ex) {
          throw new ScopeNotActiveException(beanName, scopeName, ex);
        }
      }
    }
    catch (BeansException ex) {
      beanCreation.tag("exception", ex.getClass().toString());
      beanCreation.tag("message", String.valueOf(ex.getMessage()));
      cleanupAfterBeanCreationFailure(beanName);
      throw ex;
    }
    finally {
      beanCreation.end();
    }
  }

  return adaptBeanInstance(name, beanInstance, requiredType);
}

这段代码很长,主要看我加中文注释的方法即可。

  1. 解析 Bean 的真正 name,如果 Bean 是工厂类,name前缀会加&,需要去掉;
  2. 无参单例先从缓存中尝试获取;
  3. 如果 Bean 实例还在创建中,则直接抛出异常;
  4. 如果 bean definition 存在于父的 Bean工厂中,委派给父 Bean 工厂获取;
  5. 标记这个 beanName 的实例正在创建;
  6. 确保它的依赖也被初始化;
  7. 真正创建:单例时、原型时、根据 Bean 的 scope 创建。

4.2. Spring 如何解决循环依赖问题

    请参见《深度解析源码,Spring 如何使用三级缓存解决循环依赖》。

4.3. Spring 中 Bean 的生命周期

    请参见《Spring 面试必考:Spring Bean 的生命周期和作用域》。

你可能感兴趣的:(Spring,spring,BeanFactory,BeanRegistry,BeanDefinition,ApplicationCont,Bean初始化流程)