呼伦贝尔市网站建设_网站建设公司_响应式网站_seo优化
2025/12/17 17:30:27 网站建设 项目流程

从废弃的XmlBeanFactory到XmlBeanDefinitionReader

统一术语

在开始之前,我们先明确几个关键术语:

  • BeanFactory:Spring 容器的基础接口,负责 Bean 的创建、管理和获取
  • BeanDefinition:Bean 的定义信息,包含了 Bean 的元数据(类名、属性、依赖关系等)
  • Resource:Spring 对资源的抽象,可以是文件、URL、类路径资源等
  • XmlBeanDefinitionReader:专门用于读取 XML 格式的 Bean 定义

问题场景

当我们使用 Spring 时,通常会这样加载 XML 配置:

BeanFactoryfactory=newXmlBeanFactory(newClassPathResource("applicationContext.xml"));MyBeanbean=(MyBean)factory.getBean("myBean");

但你是否思考过:

  1. 为什么XmlBeanFactory被标记为废弃?既然废弃了,为什么我们还要学习它?
  2. XML 配置是如何被读取和解析的?从 XML 文件到BeanDefinition的过程是怎样的?
  3. Spring 设计之初为什么选择了 XML 配置方式?除了 XML,还有哪些配置方式?

这些问题看似简单,但深入思考会发现,它们背后涉及 Spring 的核心设计思想。让我们从源码中寻找答案。

源码探索

第一步:认识 XmlBeanFactory

让我们先看看XmlBeanFactory的构造方法:

publicXmlBeanFactory(Resourceresource)throwsBeansException{this(resource,null);}publicXmlBeanFactory(Resourceresource,BeanFactoryparentBeanFactory)throwsBeansException{super(parentBeanFactory);this.reader=newXmlBeanDefinitionReader(this);this.reader.loadBeanDefinitions(resource);}

🔍发现一XmlBeanFactory的作用是加载 XML 配置,但实际上加载 XML 配置使用的是它组合XmlBeanDefinitionReader

这里体现了组合优于继承的设计原则:XmlBeanFactory并不直接处理 XML 解析,而是将这部分职责委托给专门的XmlBeanDefinitionReader

💡类比理解:这就像一家工厂,XmlBeanFactory是工厂老板,他不亲自去采购原材料(读取 XML),而是雇佣了一个专门的采购员XmlBeanDefinitionReader来负责。这样老板可以专注于管理工厂(Bean 管理),采购员专注于采购(配置读取),各司其职。

类图概览

在深入探索之前,让我们先看看这些类之间的关系:

classDiagram class BeanFactory { <<interface>> +getBean(name: String) Object +containsBean(name: String) boolean } class DefaultListableBeanFactory { #beanDefinitionMap: Map +registerBeanDefinition(name: String, bd: BeanDefinition) void +getBeanDefinition(name: String) BeanDefinition } class XmlBeanFactory { -reader: XmlBeanDefinitionReader +XmlBeanFactory(resource: Resource) +loadBeanDefinitions(resource: Resource) void } class BeanDefinitionReader { <<interface>> +loadBeanDefinitions(resource: Resource) int +getRegistry() BeanDefinitionRegistry } class EnvironmentCapable { <<interface>> +getEnvironment() Environment } class AbstractBeanDefinitionReader { <<abstract>> #registry: BeanDefinitionRegistry #resourceLoader: ResourceLoader +loadBeanDefinitions(resource: Resource) int* +loadBeanDefinitions(location: String) int } class XmlBeanDefinitionReader { +loadBeanDefinitions(resource: Resource) int } class GroovyBeanDefinitionReader { +loadBeanDefinitions(resource: Resource) int } class PropertiesBeanDefinitionReader { +loadBeanDefinitions(resource: Resource) int } BeanFactory <|.. DefaultListableBeanFactory DefaultListableBeanFactory <|-- XmlBeanFactory BeanDefinitionReader <|.. AbstractBeanDefinitionReader EnvironmentCapable <|.. AbstractBeanDefinitionReader AbstractBeanDefinitionReader <|-- XmlBeanDefinitionReader AbstractBeanDefinitionReader <|-- GroovyBeanDefinitionReader AbstractBeanDefinitionReader <|-- PropertiesBeanDefinitionReader XmlBeanFactory *-- XmlBeanDefinitionReader : uses note for XmlBeanFactory "已废弃 (Deprecated)<br/>Spring 5.1+ 中已标记为废弃" note for AbstractBeanDefinitionReader "使用模板方法模式<br/>定义通用流程,具体实现由子类完成"

📝图例说明

  • 实线箭头:继承关系(extends
  • 虚线箭头:实现关系(implements
  • 实线菱形:组合关系(has-a
  • 斜体方法:抽象方法(需要子类实现)

第二步:警惕"潘多拉魔盒"

XmlBeanFactory时,很容易忽略它的超类。让我们看看它的继承关系:

XmlBeanFactory extends DefaultListableBeanFactory

DefaultListableBeanFactory是一个庞大的类,包含了 Bean 注册、获取、依赖解析等大量功能。一旦深入,庞大的信息量可能会劝退首次阅读源码的朋友。

⚠️注意:但别担心,这次我们的重点并不在BeanFactory本身,而是在于XML 配置是如何定义和读取的

💡类比理解DefaultListableBeanFactory就像一本厚厚的百科全书,里面包含了所有关于 Bean 管理的知识。如果我们一开始就试图读完整本书,可能会被信息量压垮。但我们可以先专注于其中一章(XML 配置读取),等理解了这一章,再去看其他章节就容易多了。

第三步:聚焦核心——XmlBeanDefinitionReader

既然XmlBeanFactory通过组合XmlBeanDefinitionReader来扩展能力,那么我们的重点自然就落在了:XmlBeanDefinitionReader做了什么?

第四步:关注超类——吃一堑长一智

有了之前忽略XmlBeanFactory超类的教训,现在再看XmlBeanDefinitionReader时,我们自然会关注它的超类AbstractBeanDefinitionReader

💡思考时刻:看到这里,你是否也下意识地想到:这可能使用了模板方法模式?

让我们验证一下这个猜测。

第五步:探索抽象类的能力

打开AbstractBeanDefinitionReader,看看它实现了什么:

1. 实现BeanDefinitionReader接口

BeanDefinitionReader定义了一组内聚的能力:用于读取 Bean 定义。它提供了:

  • getRegistry():获取 Bean 定义注册表
  • loadBeanDefinitions():多个重载方法,用于加载 Bean 定义

特别关注loadBeanDefinitions的重载方法,可以从两个维度分类:

  • 输入形式Resource对象 或location字符串
  • 数量:单个 或 批量(可变参数)

2. 实现EnvironmentCapable接口

顾名思义,提供获取环境信息的能力。这里的环境信息是什么?暂时还不清楚,我姑且先理解它类似 Linux 环境下能通过$符号获取的环境变量,只不过 Spring 可能有它自己设置的方式。

💡类比理解EnvironmentCapable就像给程序提供了一个"环境变量表",可以存储和获取一些配置信息。比如开发环境、测试环境、生产环境的标识,或者一些系统级别的配置。这样程序就可以根据不同的环境做出不同的行为。

第六步:验证模板方法模式

好了,到这里看完了抽象类实现的接口,也就了解到了抽象类所聚合的能力。那么接下来再看抽象类是否如我所料提供了模板方法。

🔍验证结果

  • loadBeanDefinitions(Resource... resources)方法是一个典型的模板方法,提供了批量读取ResourceXML 配置的能力
  • 同样,loadBeanDefinitions(String... locations)也是类似的设计

💡类比理解:模板方法模式就像做菜的食谱。抽象类定义了做菜的步骤(1. 准备食材 2. 烹饪 3. 装盘),但具体怎么准备食材、怎么烹饪,由具体的菜谱(实现类)来决定。这样,无论是做中餐还是西餐,都可以用同一套流程,只是具体步骤不同。

关于模板方法模式的详细讲解,可以参考我的另一篇文章:1.模板方法模式

第七步:location 到 Resource 的转换

loadBeanDefinitions(String location)需要将location解析为Resource对象,然后调用具体的loadBeanDefinitions(Resource resource)

解析location存在两种情况:

  • 普通路径:直接解析为单个Resource
  • 路径表达式:由ResourcePatternResolver解析为Resource数组

ResourcePatternResolver的名字可以猜出来,它可能支持路径表达式(如通配符),这个还需要后续进一步验证。

💡类比理解location就像是一个地址(比如"北京市朝阳区"),而Resource就像是一个具体的房子。ResourcePatternResolver就像一个地址解析器,可以把地址转换成具体的房子。如果地址是通配符(比如"北京市*区"),它还能找到所有匹配的房子。

第八步:最关键的问题

💡思考时刻:好了,至此这个类就好结束了?当然没有!还差最重要的一个点:如何解析 bean 定义的配置呢?

我们再从这个抽象类里搜索下,就会发现loadBeanDefinitions(Resource resource)是一个抽象方法,需要由具体类来实现。

🔍发现:那么再回看XmlBeanDefinitionReader这个实现类,就会不禁感叹 Spring 代码设计的精妙:

  • 抽象类负责通用的流程控制(模板方法)
  • 具体类负责具体的解析实现(XML、Groovy、Properties 等)

💡类比理解:这就像是一个翻译公司。抽象类定义了翻译的流程(1. 接收原文 2. 翻译 3. 输出译文),但具体怎么翻译,由不同的翻译员(实现类)决定。有的翻译员擅长翻译 XML,有的擅长翻译 Groovy,但他们都用同一套流程。

第九步:思考 Spring 为什么选择 XML

💡思考时刻:到这里我们先不展开看如何读取 XML 配置,而是思考开头说的另一个问题:Spring 设计之初为什么选择了 XML 的配置方式?

既然这里确认是个模板方法,不如通过抽象类看看,还有什么实现:

  • GroovyBeanDefinitionReader:支持 Groovy 配置
  • PropertiesBeanDefinitionReader:支持 Properties 配置

虽然有些类已被标记为废弃,但也不难发现,除了 XML 还有其他方式进行 Spring Bean 的定义。因为市面上用的不多,我也暂时不展开阅读了。

📝总结:但这个问题值得我们思考——为什么 XML 成为了主流?这可能与 XML 在 2000 年代初期的流行程度、标准化程度、工具支持等因素有关。我们将在"设计思想"部分进一步探讨。

设计思想

1. 组合优于继承

XmlBeanFactory通过组合XmlBeanDefinitionReader来扩展能力,而不是通过继承。这样做的好处是:

  • 职责分离:BeanFactory 负责 Bean 管理,Reader 负责配置读取
  • 灵活扩展:可以轻松替换不同的 Reader(XML、Groovy、Properties)
  • 降低耦合:两者可以独立演化和测试

💡类比理解:组合就像"雇佣关系",继承就像"父子关系"。

  • 组合(雇佣):老板可以随时换员工,今天用 XML 员工,明天可以换 Groovy 员工,非常灵活
  • 继承(父子):如果用了继承,就像生了个孩子,想换也换不了,而且孩子会继承父母的所有特性,可能带来不必要的负担

这就是为什么 Spring 选择组合而不是继承的原因。

2. 模板方法模式

AbstractBeanDefinitionReader使用模板方法模式,定义了读取 Bean 定义的通用流程:

  • 抽象类定义算法骨架(模板方法)
  • 具体类实现特定步骤(如 XML 解析)

这种设计的好处是:

  • 代码复用:通用逻辑在抽象类中实现一次
  • 扩展性强:新增配置格式只需实现抽象方法
  • 符合开闭原则:对扩展开放,对修改关闭

💡类比理解:模板方法模式就像"标准化作业流程"。

  • 抽象类定义了"标准流程":1. 打开文件 2. 解析内容 3. 注册 Bean
  • 具体类只需要实现"解析内容"这一步,其他步骤都复用
  • 就像麦当劳的汉堡制作流程是固定的,但不同口味的汉堡只需要换不同的配料

关于模板方法模式的详细讲解,可以参考:1.模板方法模式

3. 为什么选择 XML?

Spring 设计之初选择 XML 的原因可能包括:

  • 时代背景:2000 年代初期,XML 是配置文件的行业标准
  • 标准化:XML 有完善的规范(DTD、XSD),支持验证
  • 工具支持:IDE 对 XML 有良好的支持(自动补全、验证)
  • 可读性:相比 Properties,XML 结构更清晰,支持嵌套
  • 扩展性:XML 支持命名空间和自定义标签,便于扩展

当然,随着注解和 Java 配置的兴起,XML 配置逐渐被替代,但理解 XML 配置的读取过程,有助于理解 Spring 的整体设计。

大白话总结

让我们用大白话再梳理一遍:

1. XmlBeanFactory 是干什么的?

📝简单说:就是一个工厂,专门用来从 XML 文件里读取配置,然后创建 Bean。

💡类比:就像一家专门生产产品的工厂,工厂需要先看"生产说明书"(XML 配置),然后按照说明书来生产产品(Bean)。

2. 为什么它被废弃了?

📝简单说:因为现在有更好的方式(注解、Java 配置),但它的设计思想依然值得学习。

💡类比:就像以前用算盘计算,现在用计算器,算盘虽然不用了,但算盘的原理(设计思想)依然有价值。

3. 它是怎么工作的?

📝简单说

  • 它自己不干活,而是找了个帮手XmlBeanDefinitionReader来读 XML
  • 这个帮手又有个"师傅"AbstractBeanDefinitionReader,教它怎么读配置
  • 师傅定了规矩(模板方法),徒弟照着做就行

💡类比:就像老板(XmlBeanFactory)不亲自干活,雇了个员工(XmlBeanDefinitionReader),员工有个师傅(AbstractBeanDefinitionReader)教他怎么做,师傅定了标准流程,员工按流程执行。

4. 为什么要这样设计?

📝简单说

  • 就像工厂流水线,每个环节各司其职
  • 如果以后要支持其他格式(比如 JSON),换个徒弟就行,师傅的规矩不用变

💡类比:就像手机充电器,接口是统一的(抽象类的模板方法),但可以换不同的充电线(不同的实现类),支持不同的充电协议。

5. 我们学到了什么?

📝简单说

  • 看源码不要只看表面,要关注它的超类和组合的类
  • 设计模式不是理论,而是实际解决问题的工具
  • 好的设计会让代码更容易扩展和维护

💡类比:就像看一棵树,不能只看树叶(表面),要看树干(超类)和树根(组合的类),这样才能理解整棵树的结构。

总结

📝本次探索收获

通过这次探索,我们:

  1. 认识了 XmlBeanFactory:虽然已废弃,但它的设计思想依然有价值
  2. 理解了组合与继承XmlBeanFactory通过组合XmlBeanDefinitionReader扩展能力
  3. 发现了模板方法模式AbstractBeanDefinitionReader使用模板方法定义通用流程
  4. 思考了设计选择:为什么 Spring 选择 XML?这背后有时代背景和实际考量

💡下一步:我们将深入XmlBeanDefinitionReader,看看它是如何将 XML 文件解析为BeanDefinition的。这将涉及 XML 解析、命名空间处理、自定义标签等更深入的内容。


思考题

  1. 除了 XML,你还知道哪些 Spring Bean 的配置方式?它们各有什么优缺点?
  2. 如果让你设计一个配置读取器,你会如何设计?为什么?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询