专栏首页星月小站Spring框架源码分析(IoC):BeanFactory和ApplicationContext容器家族

Spring框架源码分析(IoC):BeanFactory和ApplicationContext容器家族

系列文章主页

Spring框架源码脉络分析系列文章

前置知识

组件扫描:自动发现应用容器中需要创建的Bean。 自动装配:自动满足Bean之间的依赖。

BeanFactory:Spring容器的基石,顶级容器接口

在第一个章节中,我们提到过,在Spring官方文档中,称org.springframework.context.ApplicationContext这个接口就代表了Spring的容器,在解释ApplicationContext之前,必须要先介绍Spring容器的基石,BeanFactory接口。ApplicationContext就是继承了BeanFactory接口的一种高级容器接口。而BeanFactory是简单容器的代表,是Spring容器家族的基石,所有的容器都必须实现这个接口。

首先,先看一下BeanFactory接口的源码。

package org.springframework.beans.factory;

public interface BeanFactory {
	/**
	 * 对FactoryBean的转移定义,提供获取FactoryBean实例的方法。
	 * 如果定义bean时是通过工厂模式配置Bean的,那么通过bean的名字检索FactoryBean时
	 * 得到的会是FactoryBean生产出来的实例,如果想得到工厂本身,需要进行转义
	 */
	String FACTORY_BEAN_PREFIX = "&";

	/**
	 * 不同的获取Bean的方法
	 */
	Object getBean(String name) throws BeansException;
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	Object getBean(String name, Object... args) throws BeansException;
	<T> T getBean(Class<T> requiredType) throws BeansException;
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	/**
	 * 获取Bean的提供者(工厂)
	 */
	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
	
	// 检索是否包含指定名字的bean
	boolean containsBean(String name);
	// 判断指定名字的bean是否为单例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	// 判断指定名字的bean是否为原型
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	/**
	 * 指定名字的Bean是否匹配指定的类型
	 */
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	/**
	 * 获取指定名字的Bean的类型
	 */
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	@Nullable
	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

	// 获取指定名字Bean的所有别名
	String[] getAliases(String name);
}

可以看出,BeanFactory接口的源码并不复杂,主要规定了一些容器的基本功能,其中有7个获取Bean或者Bean提供者的方法,5个判断型的方法,2个获取类型的方法,1个获取别名的方法。通过这些方法,可以看出BeanFactory是一个典型的工厂模式的工厂接口。

在之前的文章中我们提到过:Spring框架的设计中,充满了通过上下继承关系来对基类进行功能扩充与功能分隔的类体系。 BeanFactory体系也是如此。

下面看一下在顶级容器接口的下面,Spring又做了哪些骚操作吧:

BeanFactory家族的核心成员主要就是上面的几个,其关系类图如图所示,BeanFactory位于家族顶层。这些接口和实现类,每一个都代表了对BeanFactory不同方向的功能扩展,下面逐一进行分析。

顶级二级接口ListableBeanFactoryHierarchicalBeanFactroy

  • ListableBeanFactory:该接口拥有列出工厂中所有Bean的能力。
public interface ListableBeanFactory extends BeanFactory {
	
	// 检索是否包含给定beanName的BeanDefinition
	boolean containsBeanDefinition(String beanName);
	// 获取工厂中BeanDefinition的数量
	int getBeanDefinitionCount();
	// 获取工厂中所有BeanDefinition的Names
	String[] getBeanDefinitionNames();
	// 获取指定类型的beanNames
	String[] getBeanNamesForType(ResolvableType type);
	String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit);
	String[] getBeanNamesForType(@Nullable Class<?> type);
	String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
	// 根据指定的类型来获取所有Bean
	<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
	<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
			throws BeansException;
	// 根据指定的直接获取beanNames
	String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
	// 获取所有指定注解标注的Bean实例,Autowired就是使用的该接口
	Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
	// 查找指定Bean中含有的注解类型
	@Nullable
	<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
			throws NoSuchBeanDefinitionException;

}

可以看出ListableBeanFactory主要对外提供了批量获取Bean和BeanDefinition的方法,拓展类了BeanFactory的功能,是一个非常重要的接口。

  • HierarchicalBeanFactroy接口:顾名思义,这是一个分层的工厂。该接口实现了Bean工厂的分层。
public interface HierarchicalBeanFactory extends BeanFactory {

	/**
	 * 返回父级工厂
	 */
	@Nullable
	BeanFactory getParentBeanFactory();

	/**
	 * 检索本地工厂是否包含指定名字的Bean
	 */
	boolean containsLocalBean(String name);

}

这个接口非常简单,也是继承自BeanFactory,虽然简单,但却提供了一个非常重要的功能——工厂分层。工厂分层有什么用呢?通过工厂分层,SpringIoC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的Bean,而父容器不能访问子容器中的Bean。在容器内,Bean的id必须是唯一的,但子容器可以拥有一个和父容器id相同的Bean。

父子容器层级体系增强了Spring容器架构的扩展性和灵活性,因为第三方可以通过编程的方式,为一个已经存在的容器添加一个或多个特殊用途的子容器,以提供一些额外的功能。

Spring使用父子容器实现了很多功能,比如在Spring MVC中,展现层Bean位于一个子容器中,而业务层和持久层的Bean位于父容器中。这样,展现层Bean就可以引用业务层和持久层的Bean,而业务层和持久层的Bean则看不到展现层的Bean。

复杂配置的Bean工厂ConfigurableBeanFactory接口

ConfigurableBeanFactory接口是一个继承了HierarchicalBeanFactroy的子接口,同时该接口还继承了SingletonBeanRegistry接口,SingletonBeanRegistry是一个用来注册单例类的接口,提供了同意访问单例Bean的功能,该接口的方法如下图:

也就是说ConfigurableBeanFactory同时拥有了工厂分层和单例注册的功能,并且为了不辜负ConfigurableBeanFactory这个名字,该接口又继续扩展了几十个方法!加上继承来的方法,这个接口中的方法数量非常之多。

该接口主要扩展了一些复杂的对单例Bean的配置与操作,虽然这个接口并没有被ApplicationContext高级容器体系所继承,但是一般的容器实现类都会继承或实现这个接口,目的是使用一种统一的方式对外暴露管理单例Bean的方式。

自动装配工厂:AutowireCapableBeanFactory接口

该接口直接继承自BeanFactory接口,扩展出了工厂的自动装配功能,其提供的方法如下:

该接口主要作用是将自动装配的能力对外暴露出来,可以通过实现此接口实现自动装配能力,但是正常情况下不应该使用该接口。此接口主要针对框架之外,没有向Spring托管的Bean的应用。其中比较重要的知识是四种自动装配策略:

	// 用于表示外部自动装配功能是否可用,但是不影响自动装配功能的使用
	int AUTOWIRE_NO = 0;
	// 标识 按名称自动装配
	int AUTOWIRE_BY_NAME = 1;
	// 标识 按类型自动装配 Autowired默认使用的该装配策略
	int AUTOWIRE_BY_TYPE = 2;
	// 标识 按照贪婪策略匹配到的最合适构造器来自动装配
	int AUTOWIRE_CONSTRUCTOR = 3;

Bean工厂接口的集大成者:ConfigurableListableBeanFactory 接口

此工厂接口看类名即可知道,是一个继承了ListableBeanFactoryConfigurableBeanFactory接口的子接口,同时它还继承了AutowireCapableBeanFactory接口,并且自身还扩展了一些功能,加在一起,这个接口的方法大概有接近100之巨。该接口总体上是继承多种父接口,并对功能进行略微扩展补充,包含了BeanFactory接口体系目前的所有方法,是Bean工厂接口的集大成者。

Bean工厂的抽象实现基类:AbstractBeanFactory抽象类

AbstractBeanFactory这个抽象类是Spring容器体系中最重要的一个抽象类,该抽象类实现了BeanFactory重要的二级接口ConfigurableBeanFactory,实现了其中的绝大多数基本方法,实现了Bean工厂的许多重要功能,如对BeanDefinition、原型、单例Bean的各种操作。

由于该抽象类实现方法繁多,逻辑复杂,若是详细分析贴出代码就容易陷入细节不能自拔,与本系列文章目的相悖,这里就不具体深入分析源码了,网上关于源码的详细分析也比较多,读者若是感兴趣,可以自行阅读与学习。

这里需要多拓展一步,更加深入对AbstractBeanFactory的了解,从源码中我们可以发现,AbstractBeanFactory其实还继承了另外一个抽象类——FactoryBeanRegistrySupport

如果我们将AbstractBeanFactory的继承关系类图打开,会发现还有另外一个体系分支:

这个继承体系是Spring的AliasRegistry体系,也就是别名注册接口体系,具体的分析见:AliasRegistry接口体系

AbstractBeanFactory既然继承了这个体系,说明容器本身也是个别名注册器、FactroyBean注册器和单例Bean注册器,也会维护别名注册表、支持FactroyBean的注册和单例Bean注册的各种操作(注册、获取、移除、销毁等)。

第一个可以独立运行的容器实现类——DefaultListableBeanFactory

DefaultListableBeanFactory类是Spring提供的默认简单容器实现类,也是从BeanFactory接口一路继承与实现下来的第一个可以独立运行的容器类,该类间接继承了AbstractBeanFactory抽象类,同时还实现了Spring集大成的容器接口——ConfigurableListableBeanFactory 接口,这保证了DefaultListableBeanFactory完整实现了简单容器中对Bean的各种操作功能。

而且我们还在源码中发现,DefaultListableBeanFactory同时还实现了BeanDefinitionRegistry接口,关于这个接口的具体介绍见:向容器中注册BeanDefinition——BeanDefinitionRegistry。正是因为实现了此接口,所以DefaultListableBeanFactory也是一个BeanDefinition注册器,拥有了注册BeanDefinition的作用。可以从源码中发现,DefaultListableBeanFactory中维护了一个beanDefinitionMap,即BeanDefinition注册表。

以上,BeanFactory简单容器体系就介绍完了,下面进入高级容器——ApplicationContext部分。

面向用户的高级容器——ApplicationContext接口体系

ApplicationContext直接翻译也被称作应用上下文,其实这个字面很难被理解,Context的意思是上下文环境,其实个人认为翻译成应用环境可能会更好一些,可以认为ApplicationContext是一种定义了维护BeanDefinition实例化对象之间协作关系 的高级接口,而这个高级接口,又是利用了Spring中普通容器来实现的,所以也被称作高级容器,也是我们平时认为的Spring容器体系,因为BeanFactroy虽然也是容器,但是平时我们直接接触不到,作为用户,平时在使用的时候,我们直接用的是ApplicationContext高级容器,也就是说,高级容器是面向用户的,而普通容器是面向框架的,是Spring容器的基石。

ApplicationContext接口继承自BeanFactory的二级接口ListableBeanFactoryHierarchicalBeanFactroy。所以高级容器其实也是在普通容器的基础上实现的,在普通容器的基础上扩展除了非常丰富的功能接口来供用户使用,如果深入去阅读源码,会发现其实在高级容器的一个很重要的抽象类AbstractRefreshableApplicationContext内部,是有一个私有的普通容器对象的,而我们常用的一些高级容器实现类,比如FileSystemXmlApplicationContextAnnotatioConfigWebApplicationContext等都继承自这个抽象类,也就是说都继承了这个普通容器对象:

这个内部的DefaultListableBeanFactory对象,通常也被称作内部容器,Spring的设计大量运用了委托模式的思想,这里在高级容器内部实例化一个内部容器出来,将普通容器的所有操作都委托给内部容器去实现,而高级容器本身只需要关注与拓展面向用户的操作即可。

下图是ApplicationContext体系类图,之后我们将展开分析一下这个体系:

高级容器的顶级接口——ApplicationContext接口

首先,ApplicationContext接口继承自ListableBeanFactoryHierarchicalBeanFactroy,也就是ApplicationContext下面的接口和类,都继承或者实现了BeanFactroy体系中的大部分功能,包括对BeanDefinition的单个与批量操作和容器分层的方法。在此基础上,ApplicationContext还同时继承了其它四个接口,对容器进行了方法的扩展,其源码如下:

额外的知识

BeanFactroy和ApplicationContext的区别

BeanFactory和FactoryBean的区别

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • spring框架通过xml以及注解方式注册BeanDefinition的流程全链路分析

    在上一章节中,主要介绍了SpringIoC、依赖注入和Spring中的Bean与BeanDefinition。可能部分读者还是比较迷茫,BeanDefiniti...

    星如月勿忘初心
  • Java实现基本数据结构(二)——栈

    阅读本文前,最好先学习顺序表的基本操作和实现原理,也就是弄清楚数组的原理,点击Java实现基本数据结构(一)——数组学习前置内容。学习效果更好哦!

    星如月勿忘初心
  • Java后端面试学习知识总结

    本系列文章是在学习Java后端知识中进行总结与考证的结晶,梳理了Java后端面试与学习的核心知识体系,并对核心知识进行了讲解,属于BFS型知识讲解,在总结的过程...

    星如月勿忘初心
  • CNCF项目超过了十亿行代码:与DevStats创造者Łukasz Gryglicki的问答

    你们中的一些人可能不知道CNCF社区有一个非常有价值的报告工具--DevStats。

    CNCF
  • 计算器(delphi)

    用户1621453
  • Java基础第二阶段知识点,招初级java的面试官都在问这些

    三哥
  • Component之CMD的0x10x10个命令

    Taishan3721
  • 一个让人遗忘的角落--Exception(一)

    很诱人的标题,今天不是给大家介绍,而是跟大家讨论些问题。 在做开发的这几年中,大大小小的项目也经历了很多,但无论那个项目中,都没有真正的对Exception进...

    脑洞的蜂蜜
  • 大数据算法设计模式(1) - topN spark实现

    topN算法,spark实现 package com.kangaroo.studio.algorithms.topn; import org.apache....

    用户1225216
  • Java基础第二阶段知识点,招初级java的面试官都在问这些

    JDK:是java开发的工具箱,包含jre,还包含将java文件编译为class文件的javac工具类(编译器),除此之外还包括java原生的API;包含J2S...

    三哥

扫码关注云+社区

领取腾讯云代金券