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

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 概述

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

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

2.2 作用

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

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

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

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

3. Spring 应用分类

3.1 概述

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

3.2 分类

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

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

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

3.3 主要实现类

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

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

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

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

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

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

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

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

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

4. 简化继承结构

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

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

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

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

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

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

5. 总结

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

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

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

  • 20
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值