初探Spring容器内幕


使用Spring也挺久啦,之前一直也停留在Spring应用的阶段,也是时候把关于Spring的一些知识点记录下来。毕竟要学习的东西太多,不记下来,太容易忘记了~

  • 前提概要
    1. 什么是Spring容器?
    2. Spring容器的分类
    3. ApplicationContext和BeanFactory是什么?
    4. ApplicationContext和BeanFactory的区别
    5. Spring容器重要接口的发展变化历史
  • 探讨BeanFactory
    1. Spring时代与SpringBoot时代
    2. BeanFactory接口源码分析
    3. 了解BeanFactory重要子接口以及层级结构
    4. BeanFactory的多种具体实现
  • 探讨ApplicationContext
    1. Spring时代与SpringBoot时代
    2. 了解ApplicationContext接口以及层级结构
    3. 常见的应用上下文(ApplicationContext)实现类
  • 相关问题
    1. 上面常常对Spring应用进行了分类,Web应用,非Web应用,Reactive应用,这是什么意思?
    2. 为什么SpringBoot 2.x需要新定义一个ServerWebApplicationContext接口?
  • 参考资料

前提概要


什么是Spring容器?

什么是Spring容器:

Spring容器是Spring的核心,一切Spring Bean都存储在Spring容器内部,实现依赖注入,并由其管理Bean的生命周期。

但是要注意的是,在代码实现层面:

  • Spring容器并不仅仅是一个容器,而是有很多个容器,Spring容器仅仅是多个容器在概念上的统称。
  • 又或者说Spring容器存在多种实现

Spring容器的分类

我们知道Spring自带多个容器的实现,这些容器大致可以分为2种类型:

  • BeanFactory(Bean工厂) BeanFacotry是最简单的容器,提供基本的DI支持

  • ApplicationContext(应用上下文) ApplicationContext是基于BeanFactory构建,并提供应用框架级别的服务,例如从属性文件中解析文本信息以及发布应用事件给感兴趣的事件监听者

通常BeanFactory是底层的实现,功能和方法很少,大部分场景仅仅是用于Spring框架的底层支持。对于业务开发者来说,BeanFactory能够提供的服务实在是少之又少,所以基本开发中,我们都是使用ApplicationContext来实现业务需求


ApplicationContext和BeanFactory是什么?

引用@《精通Spring 4.x 企业应用开发实战》的解释:

  • BeanFactory是Spring框架最核心的接口,它提供了高级IOC的配置机制,使得管理不同类型的Java对象成为可能。
  • ApplicationContext建立在BeanFacotry的基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。
  • 所以一般我们称BeanFactory为IOC容器,称ApplicationContext为应用上下文,为了方便,有时候也称ApplicationContext为Spring容器

我自己的理解:

  • Spring容器是一个概念,一个说法。ApplicationContext和BeanFactory则是它具体的代码体现
  • 也可以说ApplicationContextBeanFactory是我们与Spring容器沟通的桥梁,操纵Spring容器的工具,是Spring容器提供了api接口的对外版本,是Java操控Spring容器的cli(客户端)。

所以从上面的解释中,我们就可以简单的总结出结论:可以把ApplicationContextBeanFacotry简单的理解为Spring容器。因为你即使你要跟Spring容器交流,你也必须通过它们。


ApplicationContext和BeanFactory的区别

ApplicationContext接口继承众多接口,集众多接口功能与一身,为Spring的运行提供基本的功能支撑。(根据程序设计的“单一职责原则”,其实每个较顶层接口都是“单一职责的”,只提供某一方面的功能,而ApplicationContext接口继承了众多接口,相当于拥有了众多接口的功能) 下面看看它的主要功能:

  1. 继承了BeanFactory接口,可以管理、装配bean
  2. 继承了ResourcePatternResolver接口,是一个ResourceLoader,可以加载资源文件;
  3. 继承了MessageSource接口,可以管理一些Message实现国际化等功能;
  4. 继承了ApplicationEventPublisher接口,可以发布事件给注册的Listener,实现监听机制。

BeanFactory本身就是一个顶级接口,并没有继承其他的接口,所以功能则较为单一,则仅仅是作为IOC容器的实现,一般来说BeanFactory仅仅是用于对Bean的一些管理,在业务场景使用的是比较少的。比较BeanFactory能实现的,ApplicationContext也能实现。但ApplicationContext能实现的,BeanFactory就不一定能实现了。所以emmm…


Spring容器重要接口的发展变化历史

我们这里重点讲述Spring容器发展历史的三个阶段:

  • Spring时代
  • SpringBoot 1.x
  • SpringBoot 2.x

Spring

spring时代的Spring容器只有三个非常重要的接口:

  • BeanFactory
    IOC容器
  • ApplicationContext
    非Web应用的根应用上下文
  • WebApplicationContext
    Web应用的根应用上下文接口

SpringBoot 1.x

当进入SpringBoot时代,因为得益于SpringBoot自带嵌入式的Servlet容器,所以底层的设计也发生了一些变化。

  • BeanFactory
    IOC容器
  • ApplicationContext
    非Web应用的根应用上下文
  • WebApplicationContext
    Web应用的根应用上下文接口

重要的接口虽然没有发生变化,依然是这么三个。但是实现类却发生了一些变化。

因为使用的是嵌入式Servle容器,Servlet容器不再是外部应用,而是作为SpringBoot项目中的一个子组件的存在。所以在SpringBoot项目启动过程中,SpringBoot容器的启动和初始化是优先于Servlet容器的,Servlet容器的初始化是由Spring容器来操控的,这一点明显与Spring时代不一样。

所以Web应用的容器(WebApplicationContext)实现从

  • WebApplicationContext 变成了 dedWebApplicationContext ( 开发)
  • AnnocationConfigWebApplicationContext 变成了AnnotationConfig dedWebApplicationContext (注解开发)

SpringBoot 2.x

当为了向未来的响应式编程模式靠拢, SpringBoot 2.x提供了不同于1.x时代的非传统编程模型 - WebFlux。

  • BeanFactory
    IOC容器
  • ApplicationContext
    非Web应用的根应用上下文
  • WebApplicationContext
    SpringMVC模式下Web应用的根应用上下文接口
  • ReactiveWebApplicationContext
    WebFlux模式下Web应用的根应用上下文接口
  • WebServerApplicationContext
    为了最大公约SpringMVC和WebFlux开发模式下,Spring容器管理Web容器的规范

因为WebFlux模式是跟SpringMVC完全不同的编程模型,WebFlux甚至可以不基于Servlet容器进行Web应用开发,所以之前的核心接口就显得分类不够明确,所以核心接口也发生了一系列的变化。

由此,我们可以看到2.x 相较于1.x的版本,出现了两个新核心接口:

  • ReactiveWebApplicationContext
  • WebServerApplicationContext

因为SpringBoot 2.x 出现了开发模式的分类:

  • Spring MVC
  • WebFlux

相对1.x的WebApplicationContext实现类也发生了改变:

  • dedWebApplicationContext 变成了 ServletWebServerApplicationContext
  • AnnotationConfig dedWebApplicationContext 变成了 AnnotationConfigServletWebServerApplicationContext

两者不仅仅实现了WebApplicationContext还实现了WebServerApplicationContext接口

在WebFlux开发模式下,新增了一些接口和类:

  • 新增加了ReactiveWebApplicationContext核心接口,是相对SpringMVC模式下WebApplicationContext同级接口
  • AnnotationConfigReactiveWebServerApplicationContext是最常用的Web应用的上下文实现类
  • 没有找到相关Reactive开发的 模式应用上下文,可能已经抛弃

AnnotationConfigReactiveWebServerApplicationContext实现了
ReactiveWebApplicationContext 、WebServerApplicationContext两个核心接口

由此我们可以看出:

  • WebServerApplicationContext接口是SpringBoot 2.x中,无论是SpringMVC还是WebFlux开发模式的上下文都会去实现的一个接口。

探讨BeanFactory


Spring时代与SpringBoot时代

  • BeanFactory是一个顶级接口,是Bean的工厂,通常我们称呼它为IOC容器
  • 这里不清楚Spring -> SpringBoot 1.x -> SpringBoot 2.x时代的BeanFactory演进变化,因为功能比较底层,仅仅是作为IOC容器的实现,所以可能从Spring到SpringBoot模式的转变过程中,BeanFactory并没有发生多大的变化,主要的变化还是体现在ApplicationContext上

BeanFactory接口源码分析

  • 获取bean,这边可以实现单例,原型
	//根据Bean在容器注册的名称获取实例
	  getBean(String name) throws BeansException;
	//根据Bean注册的名称和类型获取实例
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	//根据Bean名称和构造函数的参数获取bean
	  getBean(String name,  ... args) throws BeansException;
	//根据Bean的类型获取bean
	<T> T getBean(Class<T> requiredType) throws BeansException;
	//根据Bean的类型和构造函数的参数获取Bean
	<T> T getBean(Class<T> requiredType,  ... args) throws BeansException;
  • 判断是否包含bean陷阱:这边不管类是否抽象类,懒加载,是否在容器范围内,只要符合都返回true,所以这边true,不一定能从getBean获取实例)
	//容器中是否含有该Bean
	//陷阱出现:这边不管类是否抽象类,懒加载,是否在容器范围内,只要符合都返回true,所以这边true,不一定能从getBean获取实例
	boolean containsBean(String name);
  • 单例,原型,bean类型的判断
	//该Bean是否是单例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	//该Bean是否是原型模式(通过原型模式克隆多个实例)
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	//类型判断
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	//该Bean是否是传入的typeToMatch类型
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
  • 获取bean 的类型,别名
	//获取该Bean的类型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	//获取别名
	String[] getAliases(String name);

了解BeanFactory重要子接口以及层级结构

类图基于SpringBoot 2.x :
\"在这里插入图片描述\"

重要接口:

以上是 BeanFactory的类结构图,虽然是 BeanFactory的类结构图,但是从中我们也可以看出BeanFactory的一些重要的接口和抽象实现都被列举了出来

  • BeanFactory
    Bean工厂顶级接口,提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean 别名的api.

  • HierarchicalBeanFactory
    父子级联IOC容器的接口,子容器可以通过实现该接口的方法去访问父容器,简而言之,就是提供了访问父容器的方法

  • ListableBeanFactory
    该接口定义了访问容器中Bean基本信息的若干方法,如查看Bean的个数,获取某一类型Bean的配置名,查看容器中是否包含某一个Bean等,总之类似于可以的迭代Bean的功能且不需要考虑父容器

  • AutowireCapableBeanFactory
    定义了将容器中的Bean按某种规则(如按名字匹配,按类型匹配)进行自动装配的方法,添加集成其他框架的功能

  • ConfigurableBeanFactory
    这是一个重要的接口,在HierarchicalBeanFactory接口可访问父容器的基础上,增强了IOC容器的可定制性和提供了工厂配置功能。它定义了设置类装载器,属性编辑器,容器初始化后置处理器等方法

  • ConfigurableListableBeanFactory
    该接口分别实现了ConfigurableBeanFactoryListableBeanFactoryAutowireCapableBeanFactory三个根接口(间接继承HierarchicalBeanFactory)。集大成者,提供解析,修改bean定义,并与初始化单例.

所以总结重要接口关系就是如下:

@图片来源于:Spring源码解析 - BeanFactory接口体系解读

其他相关接口:

  • SinletonBeanRegistry
    定义了允许在运行期向容器注册单实例Bean的方法
  • BeanDefinitionRegistry
    Spring配置文件中每一个<bean>节点元素在Spring容器里都通过一个BeanDefinition对象表示,它描述了Bean的配置信息。而BeanDefinition Registry接口提供了向容器手工注册BeanDefinition对象的方法

接口、抽象类的层次关系:

(第一级)BeanFacotory的直接子接口有三个:

  • HierarchicalBeanFactory接口
    拥有父容器访问功能
  • ListableBeanFactory接口
    拥有可迭代获取Bean信息的功能
  • AutowireCapableBeanFactory接口
    emmm.后面补充

(第二级别)BeanFactory真正意义上有多少主要功能的子接口

  • ConfigurableBeanFactory接口
    拥有父容器访问权限,且拥有大部分BeanFactory业务功能的真正接口

(第三级别)模板设计模式体现以及具体实现的开端

  • AbstractBeanFactory抽象类
    实现了ConfigurableBeanFactory通用的功能,根抽象类
  • AbstractAutowireCapableBeanFactory抽象类
    继承AbstractBeanFactory抽象类,实现AutowireCapableBeanFactory接口的子抽象类
  • ConfigurableListableBeanFactory接口
    继承了ConfigurableBeanFactory、ListableBeanFactory、AutowireCapableBeanFactory等接口功能的大成者

(第四级别)按需装配,具体实现类

  • DefaultListableBeanFactory具体实现类
  • BeanFactory具体实现类…

总结:

  • 设计模式和设计原则的体现:
    1. 第一步:按职责划分成多个单一职责接口
    2. 第二步:按需组装所需功能称为新的子接口
    3. 第三步:模板模式的抽象类实现,直到完成具体类
  • DefaultListableBeanFactory是通用具体BeanFactory类型

BeanFactory的多种具体实现

Spring为Bean工厂提供了多种实现,以往最常用的是:

  • DefaultListableBeanFactory
  • BeanFactory
    它从一个 文件中读取配置元数据。但是在Spring 3.1之后就已经被弃用了

建议使用:

  • BeanDefinitionReader
  • DefaultListableBeanFactory

探讨ApplicationContext

ApplicationContext是Spring应用上下文,通常我们称呼它为Spring容器


Spring时代与SpringBoot时代

Spring时代:

  • 开发
  • Java注解开发

SpringBoot时代:

  • SpringMVC 注解开发
  • SpringMVC 开发
  • WebFlux 注解开发

以上每个不同阶段所用到的ApplicationContext实现类都可以发生变化,所以要知道自己所用到的ApplicationContext具体是那个实现类,那我们就必须知道,我们是通过什么模式去开发Spring应用


了解ApplicationContext接口以及层级结构

基于SpringBoot 2.x 的ApplicationContext子接口,抽象类的层级结构图:
\"在这里插入图片描述\"

层级上:父接口

从上图以及基本概要中对应用上下文的解释,我们就可以清楚的知道ApplicationContext的确是一个集功能之大成者,它继承了非常多的具有特定功能的接口:

  • 继承了BeanFactory接口,拥有管理Bean的能力
  • 继承ApplicationEventPublisher接口,具有发布事件的能力
  • 继承EnvironmentCapable接口
  • 继承ResourcePatternResolver接口,实际间接继承ResourceLoader接口
  • 继承MessageSource接口

层级下:子接口及抽象类

(第一级)直接子接口:有三个

这一层主要是根据单一职责原则以及接口隔离原则将职责划分开来,分为多个接口

  • WebApplicationContext
    web应用的应用上下文根接口
  • WebServerApplicationContext
    web应用兼容非Servlet容器的应用上下文根接口
  • ConfiguableApplicationContext
    承载大多主要应用上下文功能的根接口

(第二级)间接子接口及抽象类: 有三个

这一层开始将上一层的划清界限的接口根据需要合并起来,成为具有特点某些功能的根接口或抽象类

  • ConfigurableWebApplicationContext接口
    web应用且拥有大多主要功能的应用上下文接口
  • ConfigurableWebServerApplicationContext接口
    web应用可兼容非Servlet容器且拥有大多主要功能的应用上下文接口
  • AbstractApplicationContext抽象类
    实现了ConfiguableApplicationContext部分通用功能的抽象类

(第三级别)体现模板设计模式的抽象类或实现类

这一层级比较特殊,开始从AbstractApplicationContext作为树的根节点下手,逐步向下延伸通用具体类型

AbstractApplicationContext开始分为一个具体类和一个抽象类,分两条线发展:

  • GenericApplicationContext具体类
  • AbstractRefreshableApplicationContext抽象类

当GenericApplicationContext子类实现了了ConfigurableWebApplicationContext接口:

  • 就成了GenericWebApplicationContext

当GenericWebApplicationContext子类实现了ConfigurableWebServerApplicationContext接口:

  • 就成了ServletWebServerApplicationContext

总结:

  • 所有接口仅仅是规范,只有抽象类和具体类才是对接口规范的实现,所以所有的实现源头就是AbstractApplicationContext抽象类
  • 从类图,我们可以看出其中的设计原则。
    1. 第一步是将职责进行分类,划分开来,分成多个具有自身特点功能的接口,细粒化开来
    2. 第二步是将细粒化后的接口根据需求组装成一个拥有多个功能的子接口,有多少不同的需求,就有多个这样的子接口,总之就是按需搭配
    3. 第三步就是根据模板设计模式,实现通用抽象类
    4. 第四步就是对通用抽象类具体化

常见的应用上下文(ApplicationContext)实现类

SpringBoot 2.x 常见应用上下文类图结构如下:
\"在这里插入图片描述\"

传统Spring中自带了多种类型的应用上下文 ,如下是常见的:

  • AnnotationConfigApplicationContext
    从一个或多个基于Java的配置类中加载Spring应用上下文,Spring配置注解开发使用
  • AnnotationConfigWebApplicationContext
    从一个或多个基于Java的配置类中加载Spring Web应用上下文,Spring配置注解开发中使用
  • ClassPath ApplicationContext
    从类路径下的一个或多个 配置文件中加载上下文定义
  • FileSystem ApplicationContext
    从文件系统下的一个或多个 配置文件中加载上下文定义
  • WebApplicationContext
    从Web应用下的一个或多个 配置文件中加载上下文定义

SpringBoot又带有自己的一些上下文,如:

  • AnnotationConfig dedWebApplicationContext
    SpringBoot 1.x 项目中替代AnnotationConfigWebApplicationContext作为WebApplicationContext的默认实现类,因为以前的WebApplicationContext的初始化是由Servelt容器完成的,而SpringBoot中Spring容器初始化优先于Servlet容器
  • dedWebApplicationContext
    SpringBoot 1.x项目中替代 WebApplicationContext
  • AnnotationConfigServletWebServerApplicationContext
    SpringBoot 2.x 项目中替代AnnotationConfig dedWebApplicationContext,作为WebApplicationContext的默认实现类
  • ServletWebServerApplicationContext
    SpringBoot 2.x项目中替代1.x 的 dedWebApplicationContext
  • AnnotationConfigReactiveWebServerApplicationContext
    SpringBoot 2.x 项目中的WebFlux模式的ReactiveWebApplicationContext实现类,跟传统MVC的WebApplicationContext是同级不同编程模式的Spring容器

总结:

这些Spring容器(应用上下文)通常默认情况下仅仅会生成一个,都是同级容器,一般不会共存

  • 在Spring时代的 开发模式中
    1. Web应用默认的实现的应用上下文是 WebApplicationContext
    2. 非Web应用则有两个选择:ClassPath ApplicationContextFileSystem Application
  • 在Spring时代的注解开发模式中
    1. Web应用默认实现的应用上下文是AnnotationConfigWebApplicationContext
    2. 非Web应用则是AnnotationConfigApplicationContext
  • 在SpringBoot 1.x的 开发模式中
    1. Web应用默认实现的应用上下文是 dedWebApplicationContext
    2. 非Web应用是AnnotationConfigApplicationContext
  • 在SpringBoot 1.x的注解开发模式中
    1. Web应用默认实现的应用上下文是AnnotationConfig dedWebApplicationContext
    2. 非Web应用是没在意~,应该还是ClassPath ApplicationContextFileSystem Application
  • 在SpringBoot 2.x的 开发模式中
    1. Web应用默认实现的应用上下文是 ServletWebServerApplicationContext
    2. 非Web应用是没在意~,应该还是ClassPath ApplicationContextFileSystem Application
  • 在SpringBoot 2.x的注解开发模式中
    1. Web应用默认实现的应用上下文是AnnotationConfigServletWebServerApplicationContext
    2. Reactive Web应用是或AnnotationConfigReactiveWebServerApplicationContext
    3. 非Web应用是AnnotationConfigApplicationContex

相关问题


上面常常对Spring应用进行了分类,Web应用,非Web应用,Reactive应用,这是什么意思?

在SpringBoot 2.x以前,Spring应用只有两个类型:

  • Web应用
  • 非Web应用

SpringBoot 2.x开始,因为引入了WebFlux,所以将Web应用加以区分

  • 非Web应用
  • Web应用 (传统Spring MVC Web)
  • Reactive 应用 (WebFlux)

什么是Web应用,什么是非Web应用呢?

Web应用就是我们传统的Web工程,非Web应用就是不需要Web容器的普通Java项目。意思就是说Spring本身是支持非Web工程使用Spring生态技术的。比如你自己建一个Java工程,实现某些功能,但是可以使用Spring的ioc容器去管理bean,aop去实现切面编程


为什么SpringBoot 2.x需要新定义一个ServerWebApplicationContext接口?

首先我们知道SpringBoot 2.x引入了WebFlux开发模式,其次我们看一下新接口WebServerApplicationContext 的源码:

WebServerApplicationContext源码:

/**
 * Interface to be implemented by {@  ApplicationContext application contexts} that
 * create and manage the lifecycle of an  ded {@  WebServer}.
 * 
 * 用于创建和管理嵌入式Web容器的生命周期 
 *
 * @author Phillip Webb
 * @since 2.0.0
 */
public interface WebServerApplicationContext extends ApplicationContext {

	/**
	 * Returns the {@  WebServer} that was created by the context or {@code null} if
	 * the server has not yet been created.
	 * @return the web server
	 */
	WebServer getWebServer();

	/**
	 * Returns the namespace of the web server application context or {@code null} if no
	 * namespace has been set. Used for disambiguation when multiple web servers are
	 * running in the same application (for example a management context running on a
	 * different port).
	 * @return the server namespace
	 */
	String getServerNamespace();

}

我们发现:

  • 该接口的作用是用于创建和管理嵌入式Web容器的生命周期
  • 只有两个方法,得到WebServer和得到ServerNamespace

所以我们发现WebServerApplicationContext所有操作都是依赖WebServer接口去实现,那这是什么呢?我们再来看一下源码:

WebServer源码

/**
 * Simple interface that represents a fully configured web server (for example Tomcat,
 * Jetty, Netty). Allows the server to be {@  #start() started} and {@  #stop()
 * stopped}.
 * 
 * 操作Web容器的客户端,也可以简单理解为Web容器本身
 *
 * @author Phillip Webb
 * @author Dave Syer
 * @since 2.0.0
 */
public interface WebServer {

	/**
	 * Starts the web server. Calling this method on an already started server has no
	 * effect.
	 * @throws WebServerException if the server cannot be started
	 */
	void start() throws WebServerException;

	/**
	 * Stops the web server. Calling this method on an already stopped server has no
	 * effect.
	 * @throws WebServerException if the server cannot be stopped
	 */
	void stop() throws WebServerException;

	/**
	 * Return the port this server is listening on.
	 * @return the port (or -1 if none)
	 */
	int getPort();

}

我们发现:

  • 这个WebServer其实就是Web容器,用于操作和管理Web容器的根接口

那么为什么需要WebServerApplicationContext ?

  • 我们从WebServerApplicationContext 和WebServer 接口的源码也可以看出,实现这些接口的目的就是为了不管是SpringMVC还是WebFlux模式下的Spring容器,有统一的接口去管理Web容器。因为传统MVC模式下的Spring容器时通过实现WebApplicationContext接口的getServletContext方法去管理Web容器的

  • 我们知道SpringMVC模式是基于Servlet开发的,但是WebFlux可以不基于Servlet。所以SpringBoot 2.x的项目可能会存在不同的Web服务器实现。而之前的WebApplicationContext不支持对非Servlet容器的管理(因为这个接口只有与ServletContext相关的方法)。所以迫切需要一个可以兼容Servlet容器和非Servlet容器(如Netty)的方法支持。所以WebServerApplicationContext就出现了

  • 我个人觉得这里沿用的是策略模式的设计,将对获取Web容器的行为抽象化到一个接口,WebServerApplicationContext代表一类拥有获取Web容器的上下文接口。而对WebServer的具体实现则是要传入的策略,如你到底是传入TomcatWebServer或者NettyWebServer或其他实现类。


参考资料


收藏 打印