第二篇 再读Spring 之 BeanDefinition解析


文章目录

  • 第二篇 再读Spring 之 BeanDefinition解析
  • 一、颗粒度问题
  • 二、细说Spring中不同颗粒度对象在解析中的作用
    • 1. 先说BeanDefinitionRegistry
    • 2.(容器级)BeanDefinitionReader
    • 3.(文件级)BeanDefinitionDocumentReader
    • 4.(标签级)BeanDefinitionParserDelegate
  • 三、颗粒粘合
  • 四、总结

一、颗粒度问题

以Spring的XML配置文件为例,日常工作中现有applicationContext.xml 然后在文件中添加标签配置。显然就BeanDefinition加载而言,需要对Document(文档)和Element(元素)两种颗粒度的处理。特别地,我们的一个应用可能包含多个配置文件,多个配置文件包含了整个ApplicationContext中的bean。显然,这是一个更大的颗粒度,也就是容器级。从元素,文档到容器,我认为这是一次系统颗粒度的划分和识别,更抽象来说,这就是颗粒度问题。那为什么需要注意颗粒度问题?

不同颗粒级别就是对业务的一种拆分,拆分就意味着可能重用。毕竟软件工程的两大原则就是分解和重用。所以,良好的颗粒度识别和划分是系统设计或者架构的第一步。如果有看过UI/UE去设计网页,首先要做的就是页面布局,也就是页面上该划分几块,每块的内容是什么。如果用数学描述,这算是某种程度上的离散化。

因此,合理的颗粒度划分非常关键。Spring对BeanDefinition的加载处理就是按照这种颗粒度来组织加载逻辑的。

二、细说Spring中不同颗粒度对象在解析中的作用

1. 先说BeanDefinitionRegistry

想容纳所有的BeanDefinition,我们需要一个容器,这个容器提供方法,向其中加入/删除BeanDefinition,这就是BeanDefinitionRegistry,后续讨论中我们认为BeanDefinitionRegistry就是我们的BeanDefinition容器。从颗粒度的角度,这里是容器级。

BeanDefinitionRegistry中对应的方法是:

// 注册(添加)BeanDefinition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException;
// 删除BeanDefinition
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

2.(容器级)BeanDefinitionReader

仅仅拥有容器是不够的,因为没有实体调用registerBeanDefinition方法,去添加BeanDefinition。所以需要一个调用者,在Spring中就是BeanDefinitionReader,对于XML文件配置的应用来说,具体实现类为XmlBeanDefinitionReader. 该类掌控全局完成所有的BeanDefinition加载。之所以强调全局,是因为这里要放全局也就是跨XML文件级别的逻辑。具体有哪些呢?

  1. XML文件之间可以通过import建立关联,这里会有循环依赖,所以需要有循环依赖的侦测和处理逻辑。不过考虑到Spring通过Resource对各种配置形式做了抽象,所以准确说,应该是resource之间循环依赖的侦测和处理;
  2. 加载过程中的错误报告收集;
  3. 全局的自定义标签处理器NamespaceHandlerResolver;
  4. 全局的DocumentLoader,将Resource转换为Document;

3.(文件级)BeanDefinitionDocumentReader

具体到单个XML文件的处理,则是由BeanDefinitionDocumentReader来完成,具体实现是DefaultBeanDefinitionDocumentReader。这个类有3点需要注意:

  1. 预留了2个扩展方法,留给子类做扩展;
protected void preProcessXml(Element root) {}
protected void postProcessXml(Element root) {}
  1. 通过XmlReaderContext接收全局级别对象
    该类中并未持有对XmlBeanDefinitionReader的引用,而是通过XmlReaderContext间接访问到BeanDefinitionReader中的全局对象。如果是我来实现肯定在初始化DefaultBeanDefinitionDocumentReader时,把 BeanDefinitionReader 的this带过去。但是从OOP的角度,“局部"作为"下层"没必要知道自己在哪个"全局”(上层)中,所以直接带this是不合适的。既然没必要知道,那连Context也没必要要,为什么要加呢?后面再聊。

  2. 元素级别的上下文关联
    对于嵌套的Beans标签,在处理完成后,处理上下文需要恢复到包含Beans的上级标签。这似乎符合栈数据结构的使用场景,FILO。不过Spring不是这么做的,由于只有两级,直接使用parent记录父级上下文,Beans标签处理完成后,将当前处理上下文恢复。仔细想来,3个层级以内的,这么搞应该都是可以的。

BeanDefinitionDocumentReader的核心方法:

protected void doRegisterBeanDefinitions(Element root) {// parent 缓存 当前delegete,然后当前delegate 更新为子级别的delegate BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isDebugEnabled()) {logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}// 预留的扩展方法preProcessXml(root);// 完成具体解析的方法parseBeanDefinitions(root, this.delegate);// 预留的扩展方法postProcessXml(root);this.delegate = parent;}

4.(标签级)BeanDefinitionParserDelegate

到了标签级别,除了自定义标签,主要有4大类,import, bean, beans 和alias. 这里除了import外,其他的标签都可以在当前上下文中解析。为啥import不行呢?

因为import指向的是另外一个XML文件,显然这里需要文件级别的对象提供支持,但是偏偏被标签级别的解析器处理。小马拉大车显然不行,考虑到逻辑重用,Spring得去重用文档级别上的处理逻辑,但是怎么比较符合OOP的方式调用到对应的方法是个问题。Spring是通过ReaderContext解决的,此处也解释了为什么需要1?

  1. 业务上有调用父级逻辑的需求;
  2. 站在OOP的角度,不能直接持有父级的引用,但需要一个暂存的对象来间接持有,这就是类Context对象的作用—关联上下层级;
  3. 有了Context之后,还可以把一些扩展逻辑放在其中;

类似的也可以考虑ApplicationContext,连接BeanFactory和程序对外的一些扩展功能。

三、颗粒粘合

对系统划分颗粒之后,还要把这种颗粒给粘合起来,否则就是一盘散沙。如何把颗粒之间的粘合起来,就是要确定颗粒之间的关联关系。也就是两个颗粒之间什么关系,是否需要关联的,该以何种方式关联。结合个人对Spring源码的阅读来聊聊这个问题。

将不同颗粒度的对象的关联为一个整体,就像树结构一样,从树根开始长出树枝,树枝上长出树叶。抽象到数据结构中,树枝是树根一级子节点,树叶则是树根的二级子节点,当然更复杂的可能有更多级。显然,每一级在逻辑上对应更小的颗粒度,上一层级关联下一层级,直到最小的不可再分割的颗粒度。

在实际工程中,关联有单双向区分。而类似BeanDefinition解析过程,需要下层访问上层,意味着双向访问。此外,有了双向访问后,同一个树根下的子节点可以借助树根调用到兄弟节点的逻辑。最终可能是,任何层级节点,可以调用其他层级的节点,这种自由度,可能是单纯的树结构无法做到的,当然也会更加复杂。

四、总结

以上就是今天要聊的内容,从颗粒化的作用,Spring中颗粒度的划分,层级之间双向关联的处理,最后到回归系统整体角度,看对不同层级颗粒对象的粘合。一方面对Spring BeanDefinition 的解析过程有一个清晰的脉络,也对设计模式的应用提供参考案例。

第二篇 再读Spring 之 BeanDefinition解析相关推荐

  1. 第四篇 再读Spring 之BeanDefinition注册

    文章目录 前言 一.管理的必要性 框架设计 工程实现 二.Spring的实现 1.抽象接口BeanDefinitionRegistry 2. 具体实现DefaultListableBeanFactor ...

  2. Spring IOC BeanDefinition解析

    Spring IOC BeanDefinition解析 IOC(Inversion of Control)即控制反转,是说创建对象的控制权进行了转移,以前创建对象的主动权和创建时机是由自己把控的,而现 ...

  3. 【Java】【系列篇】【Spring源码解析】【三】【体系】【BeanFactory体系】

    BeanFactory体系 BeanFactory整体结构体系图 顶层接口-BeanFactory 1.1.描述 1.2.方法解析(15个) 1.2.1.属性 1.2.2.获取bean实例 1.2.3 ...

  4. 商业模式第二篇:另外十种商业模式解析

    在商业管理领域,我们试图寻找某种工具或方法以帮助完成商业模式创新这项最困难的任务,却都毫无收获.这使得花费了数年时间自行研究创新模式的设计方法,并在广泛社会种实践检验. 自上次的10种常见的商业模式之 ...

  5. 第二篇:读曹德旺《心若菩提》

    最近看曹德旺的自传<心若菩提>,里面有件事情让我印象特别深刻,就是正直. 01曹德旺的选择 曹德旺在一次次面对利益的选择时,都选择了坚守自己的正直.而代价是一次次损失金钱,最多的一次损失高 ...

  6. 长春牙齿矫正日记第二篇-----------洗牙以及口腔扫描

    连载---------------第二篇 再决定了选择哪一种牙套之后,我第二次来到了这家牙齿矫正医院(长春欣雅口腔). 首先付款之后签订协议,一共2万3,不包括智齿的拔牙和一些补牙,我选择了分期付款( ...

  7. 十年后2023年再读这篇文章,看看我将会怎么样?

    看到一篇文章不错[清华差生10年奋斗经历] ,写给将要工作的自己,十年后2023年再读这篇文章,看看我将会怎么样? 在2012年收关时刻,看到如此激励的文章,实在是我的幸运.文章讲述了所谓清华差生的奋 ...

  8. 十年后2023年再读这篇文章,看看我将会怎么样

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 看到一篇 ...

  9. spring IOC 之篇三:默认标签的解析

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {// 对import标签的处 ...

最新文章

  1. 网络流—Edmonds-Karp 最短增广路算法(最大流)
  2. BZOJ 1096: [ZJOI2007]仓库建设
  3. ubuntu18.04下编译mysql5.7源码
  4. 【计算理论】计算复杂性 ( 证明 非确定性图灵机 与 确定性图灵机 的时间复杂度 之间的指数关系 )
  5. 提供openssl -aes-256-cbc兼容加密/解密的简单python函数
  6. redis 使用管道提升写入的性能[pipeline]
  7. 不飘了,让图像识别算法快速产业落地 别再实验了,让你的图像识别算法赶紧上线!...
  8. Java中值传递和引用传递原理以及区别
  9. 用VB.NET(Visual Basic 2010)封装EXCEL VBA为DLL_COM组件(二)
  10. 【剑指offer】面试题40:最小的k个数(java)
  11. 【VRP】基于matlab遗传算法求解出租车网约车接送客车辆路径规划问题【含Matlab源码 YC003期】
  12. Hadoop权威指南:知识梳理(一)
  13. 03年用友R9是什么语言写的_用友财务软件使用什么语言开发的?
  14. SQL server 数据迁移到mysql
  15. matlab ode45求解齿轮动力学,Matlab讨论区 - 声振论坛 - 振动,动力学,声学,信号处理,故障诊断 - Powered by Discuz!...
  16. 使用scrapy爬取北京公交
  17. JAVA毕业设计花卉网站计算机源码+lw文档+系统+调试部署+数据库
  18. 任意斜率的中点画线算法
  19. 计算机技术考研科目大纲,2017计算机考研大纲:计算机大纲文字完整版
  20. 单片机C51液晶显示器LM016L的操作方法

热门文章

  1. SpringGateway 网关
  2. 诺贝尔物理学奖变身“理综”奖:乍看颁给全球变暖研究,其实背后通用理论模型影响机器学习...
  3. 去中心化的滴滴打车派单模拟系统前端历程
  4. python-课后作业-4
  5. 小写字母转换成大写字母(函数)
  6. 2012年下半年软件评测师上午试题
  7. Linux内核驱动初探(二) TI声卡
  8. 中国信通院苏丹等:5G+自动驾驶技术专利态势分析
  9. jacoco 原理篇
  10. c语言编程斐波那契前n项,c语言:写一个函数,输入n,求斐波拉契数列的第n项(5种方法,层层优化)...