likes
comments
collection
share

【重写SpringFramework】ApplicationContext概述(chapter 3-1)

作者站长头像
站长
· 阅读数 19

1. 前言

IOC 和 AOP 是 Spring 框架的两大核心机制,我们已经在 beans 和 aop 模块中有过详细论述了。尤其是 beans 模块,其基础性表现在各个分散的功能中,并不适宜直接被用户(开发者)使用。鉴于此,Spring 提供了 context 模块整合了 beans 和 aop 模块,不仅隐藏了底层的细节,还提供了一系列强大、易用的功能,对于用户(开发者)十分友好。在实际使用中,开发者在和 context 模块的 API 打交道,只有很少的操作可能会涉及到底层模块。

ApplicationContext 的字面意思是「应用上下文」,应用是指开发者编写的代码,上下文是什么意思?我们在阅读文章的时候,为了确定某个词或某句话的意思,通常需要在整个段落或者相邻的几个段落中进行考察,这一方法被称为联系上下文。从这里引申出来,应用上下文是指程序在运行时所依赖的环境的总和。通俗易懂地说,有了 ApplicationContext,就具备了编写应用程序的基础。

有人可能会问,编写程序的基础不应该是 beans 和 aop 模块吗?准确地说,这几个模块(还有 expression 模块)是基础的基础,因为太过基础反倒不适合被直接使用。ApplicationContext 实际充当了对外沟通的门面,屏蔽了更为基础的模块,提供一站式的解决方案,显著地提高了开发的效率。

Spring 容器的概念有狭义和广义之分,狭义上指的是 BeanFactory,广义上指的是 ApplicationContext。不论是狭义的还是广义的,它们都是各自模块的核心接口,两者之间存在千丝万缕的联系。因此在开始介绍 ApplicationContext 之前,我们有必要搞清楚它们的关系。

2. 装饰模式

2.1 概述

ApplicationContextBeanFactory 之间不是简单的组合,而是通过「装饰模式」联系在一起的。装饰(Decorator)模式又名包装模式,主要作用是扩展对象的功能,可以作为继承的替代方案。装饰模式的结构包含四个角色,ApplicationContext 的继承结构中也有对应的接口和类,简单分析对比如下:

  • 抽象组件角色:BeanFactory 定义了管理 Bean 的相关方法,比如 getBean 等方法
  • 具体组件角色:DefaultListableBeanFactory 实现了 getBean 等方法
  • 抽象装饰角色:ApplicationContext 继承了 BeanFactory 接口,此外还定义其他扩展功能
  • 具体装饰角色:GenericApplication 实现了 ApplicationContext 接口,持有一个组件类。此外还实现了 getBean 等方法(实际上委托给 DefaultListableBeanFactory 处理 )

【重写SpringFramework】ApplicationContext概述(chapter 3-1)

2.2 作用

装饰模式有两个基本特点,一是由组件类提供核心功能,由装饰类负责外围的工作。二是装饰类往往不止一个,不同的情况需要不同的装饰类来处理。在 Spring 的整个体系中,最基础、最根本的工作是管理单例,BeanFactory 始终围绕这一点展开。ApplicationContext 作为装饰类不仅继承了 BeanFactory 的能力,还拥有很多强大易用的扩展功能。

举个例子,BeanFactorygetBean 方法为入口,完成了 Bean 的实例化、初始化、依赖注入等操作,唯独缺少了关键的一步,BeanDefinition 从哪里来?创建 BeanDefinition 需要知道类信息,这又涉及到源文件(字节码)的定位、加载和解析,这些工作都是由 ApplicationContext 完成的。在创建 Bean 的整个过程中,管理 Bean 的操作是不变的,加载 Bean 的操作是变化的。不变的部分由组件类 BeanFactory 来实现,变化的部分作为扩展功能交给装饰类 ApplicationContext 处理。BeanFactoryApplicationContext 各司其职,很好地契合了装饰模式的第一个特点。

同样地,我们也可以反向思考,假如没有 ApplicationContext,加载组件的功能由 BeanFactory 来实现会发生什么?context 模块中的大部分内容都与加载组件有关,相当一部分代码会塞入 beans 模块中,使得原本复杂的情况更加糟糕,结果就是逻辑混乱、主次不分,不利于代码的阅读和维护。从宏观来看,context 模块还整合了 aop 和 expression 模块,最终合并后的 beans 模块将会是现有四个模块的体量,从而变得臃肿不堪。

【重写SpringFramework】ApplicationContext概述(chapter 3-1)

以上正反两方面的分析可以发现,这实际上是对单一职责原则的应用。从微观层面(类和接口)来说,BeanFactoryApplicationContext 各司其职,对复杂操作进行合理地分解。从宏观层面(模块和库)来说,beans 和 context 模块内外有别,context 模块是面向用户的,需要更多地体现灵活性。

3. Spring 应用分类

3.1 概述

在介绍 BeanFactory 时,我们对其庞杂的继承结构进行精简,并根据不同的功能进行分组。总的来说,BeanFactory 的功能较为单一,主要是对 Bean 进行管理,而 ApplicationContext 是面向用户的,需要处理各种各样的情况。相比之下,ApplicationContext 继承结构的复杂度有过之而无不及。因此,我们的首要任务是对庞大的继承结构进行分析和梳理,只有从宏观层面认识了整体情况,研究源码就不再是无的放矢。

【重写SpringFramework】ApplicationContext概述(chapter 3-1)

3.2 分类

ApplicationContext 的众多实现类中,我们可以从两个维度来划分,一是应用类型(用途),二是配置方式。先来看应用类型,一般来说可以分为两大类:一是普通应用,二是 web 应用。其中,web 应用又可以分为传统 web 应用与内嵌 web 应用,简单介绍如下:

  • 普通应用:常用于编写测试代码,或者使用 Swing 等框架编写 GUI 程序
  • 传统 web 应用:使用 servlet 容器作为最外层服务,可以同时运行多个应用,比如部署在 Tomcat 工作目录下的应用程序(war 包)
  • 内嵌 web 应用:应用程序独立运行,servlet 容器作为一个组件嵌入在应用程序中(jar 包)

【重写SpringFramework】ApplicationContext概述(chapter 3-1)

其次,Spring 应用根据配置方式的不同还可以分为两类。一是基于配置文件,主要是 XML 文件;二是基于注解声明的配置方式。在 Spring Boot 大行其道的今天,基于 XML 文件配置的应用很少见了,一些老的项目可能还在使用。我们关心的是使用注解配置的实现类,对于基于 XML 文件配置的实现类,只需要知道有这么一回事就可以了。

3.3 主要实现类

搞清楚了两个维度的划分标准之后,我们来看一些常见的实现类。按照应用类型分为两组,第一组是普通应用的实现类,如下所示:

  • FileSystemXmlApplicationContext:文件系统中的 XML 配置文件(路径是任意的)

  • ClassPathXmlApplicationContext:类路径下的 XML 配置文件(路径是固定的)

  • AnnotationConfigApplicationContext:使用注解声明的普通应用

【重写SpringFramework】ApplicationContext概述(chapter 3-1)

第二组是 web 应用的实现类,其中前两个类仅了解,后两个类将在后续章节中进行介绍。

  • XmlWebApplicationContext:使用 XML 配置文件的传统 web 应用

  • XmlEmbeddedWebApplicationContext: 使用 XML 配置文件的内嵌 web 应用

  • AnnotationConfigWebApplicationContext:使用注解声明的传统 web 应用

  • AnnotationConfigEmbeddedWebApplicationContext :使用注解声明的内嵌 web 应用

4. 简化继承结构

通过上述分析,我们对 ApplicationContext 的原始继承结构进行精简。首先,剔除了基于配置文件的实现类,这些内容占据了大多数篇幅。其次,从用途上来说,ApplicationContext 的实现类可以分为三组。这三种类型的实现都是我们所关心的,本章仅涉及第一种类型,也就是普通应用的实现。

经过梳理得到一个简化的继承结构,如下图所示,分为三个部分。蓝色部分代表本模块所涉及的接口和类,实现了 ApplicationContext 的基本逻辑。紫色部分表示传统 web 应用的相关接口和类,这部分内容将在第五章 web 模块中讲解。黄色部分代表内嵌 web 应用的相关接口和类,这部分内容将在第二部 Spring Boot 中讲解。

【重写SpringFramework】ApplicationContext概述(chapter 3-1)

这三部分各有一个核心的实现类,我们将在后续内容中逐一进行介绍。

  • AnnotationConfigApplicationContext:基于注解的普通应用,本模块唯一的实现类,主要用于测试代码,有时也会使用 GenericApplicationCointext 代替

  • AnnotationConfigWebApplicationContext:基于注解的传统 web 应用(将在第五章 web 模块中讲解)

  • AnnotationConfigEmbeddedWebApplicationContext:基于注解的内嵌 web 应用(将在第二部 Spring Boot 中讲解)

5. 总结

本节介绍了 context 模块的定位和基本作用,主要表现在整合 beans 和 context 等模块,屏蔽了底层的实现细节,充当了面向开发者的门面。ApplicationContext 作为 context 模块的核心接口,是广义的 Spring 容器的实现。BeanFactoryApplicationContext 之间有千丝万缕的联系,通过装饰模式组合在一起,各司其职。其中,BeanFactory 负责核心工作,主要是对 Bean 进行管理。在这一基础上,ApplicationContext 提供了一系列强大易用的功能,显著地提高了开发效率。

ApplicationContext 的继承结构十分庞大,我们从两个维度来进行划分。首先从用途上来说,分为普通应用和 web 应用,其中 web 应用继续分为传统 web 应用和内嵌 web 应用。其次,从配置方式来说,分为配置文件和注解声明两种方式。需要指出的是,配置文件的方式是一种比较老旧的技术,现在通常使用注解声明的方式来编写 Spring Boot 应用。

综上所述,我们关心的是基于注解声明的 ApplicationContext 实现。具体到应用的类型,本章只涉及普通应用,也就是 AnnotationConfigApplicationContext 实现类。至于另外两个实现类,我们将在传统 web 应用和内嵌 web 应用的相关内容进行介绍。


项目地址:gitee.com/stimd/sprin…

欢迎关注公众号【Java编程探微】,加群一起讨论。

原创不易,觉得内容不错请关注、点赞、收藏。

转载自:https://juejin.cn/post/7398087074971287567
评论
请登录