Spring InitBean

本文深入探讨了Spring框架的初始化过程,包括资源加载、解析及注册等关键步骤,并详细介绍了涉及的核心类及其作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

初始化过程

无非是读取->解析->注册,相信程序员都能实现它,但是要把它变成一个框架性的工具还是需要走很多路的。

  1. load 即资源加载,具体说就是找到合适的工具读取合适的资源(不同的方法读取不同的资源,下一篇有详解)

  2. parse 即资源解析,这里的标准比较多,先是根据读取XML中的关键字“DTD”来选择XML资源的校验模式,不知道是不是我代码读错了,我觉得有点土诶;然后根据相对应的实体解析类(这里没仔细研究,但是可以自定义shema标准去拓展)生成DOM;再托管给documentReader去解析DOM生成一个个BEAN;最后代理给BeanDefinitionParserDelegate去解析一个个的BEAN,生成BeanDefinition(这里其实被包装了一下)。

  3. registor 即注册,首先XMLBeanFactory说代理也好,说回调也好,说托管也好,方式都是把自己传下去了,最然过程中自己变样了,从XMLBeanFactory到BeanDefinitionRegistry到XmlReaderContext,最后回到BeanDefinitionRegistry(注意BeanDefinitionRegistry和XMLBeanFactory的区别,而且XMLBeanFactory就逻辑上而言没有传到BeanDefinitionParserDelegate中,虽然代码中有这样写到,具体原因是因为BeanDefinitionParserDelegate只负责将Bean变成BeanDefinition),最后将上一个阶段解析好的BeanDefinition注册到一个MAP中交给XMLBeanFactory来管理。

类图

102858_i9NJ_947581.jpeg

该类的核心是XmlBeanDefinition,该类相关的:左面两个类真是SpringExt拓展的类,提供XSD资源加载策略和Bean的解析策略等;右面两个类可以用第三方替换,旨在提供解析DOM的实现;下面那个类主要是依据DOM解析成一个个的BeanDenifition,主要定义了一些默认的行为。

  • 框架中主要体现的类大致分为三类:控制类、环境类和行为类(control、context、parse/handle)

  • 环境类有三种,分别是:BeanDefinitionRegistry,XmlReaderContext和ParserContext,且都持有BeanDefinition的引用

  • 行为类有:BeanDefinitionParserDelegate,BeanDefinitionParser,NamespaceHandler,EntityResolver

  • 控制类有:XmlBeanDefinitionReader,BeanDefinitionDocumentReader,NamespaceHandlerResolver

详解

从BeanFactory来详解初始化过程,毕竟ApplicationContext的初始化只是一个inner BeanFactory,核心也是利用的BeanFactory来实现的。

XmlBeanDefinitionReader

104913_bMwj_947581.jpeg

  • resourceCurrentlyBeingLoaded变量的作用:线程本地的变量保存了已经读取的配置文件,以防循环加载(import会触发)

  • 最后的关闭流之前其实流已经被关闭了,因为框架采用了jaxp的dom方式的xml解析,在最后已经被关闭,因此在拓展的时候要注意这一点。

  • 这个类是用来拓展spring的入口点,回顾前面的类图,分析下该类会发现:该类关联了左面两个类,右面两个类和下面一个类。左面两个是用来定义xsd文件的加载策略和解析策略的(正是x3的精髓所在);右面两个类是用来解析xml的,当然你可以哟过sax的解析方法替换之;下面一个类是把解析完的dom二次解析为一个个的BeanDefinition。其实这五个点你都可以替换。

XmlBeanDefinitionReader

105022_Xpt1_947581.jpeg

  • 莫要被这个方法简单的几行所蒙蔽,这三行代码都很值得研究,尤其是第二行,哈哈~这个折腾了我好久,其实最后还是没怎么看懂。

  • 首先是判断文档需要采用dtd验证还是xsd验证,具体实现不难,基于字符验证的。

  • 然后采用jaxp的dom方式来解析xml,getEntityResolver()这个入参既是把我们自定义的dtd/xsd文件的加载策略嵌入进去,所以X3框架的xsd可以采用spring包里面自带的,也可以采用webx3-core里面自带的,也可以采用工程meta目录下面的,大家有兴趣的可以去看看。

XmlBeanDefinitionReader

105137_2qt5_947581.jpeg

  • 构建dom解析的上下文:xmlReaderContext,因为持有NamespaceHandlerResolver因此就具有解析成BeanDefinition的能力,有个细节注意下,此上下文依旧持有BeanDefinitionRegister的引用,为的就是最后的注册

  • 这个方法是第二个拓展点:NamespaceHandlerResolver的入口点

BeanDefinitionDocumentReader

105320_uGQz_947581.jpeg

  • 拓展点很明显,是前置处理器和后置处理器

  • 创建了个代理去解析dom,其实这也是一个control-action的模型,BeanDefinitionDocumentReader处理的是dom树,而代理处理的是每一个节点,因此具体的实现都是在代理类之中。

BeanDefinitionDocumentReader

105447_Urk6_947581.jpeg

  • 默认的命名空间和自定义的命名空间的解析方式差距还是挺大的,默认的用不到NamespaceHander,具体的解析实现都在BeanDefinitionParseDelegate中;自定义的命名空间的BeanDefinition的解析过程:NamespaceHandlerResolver解析出具体的NamespaceHandler,然后依据该handler取出具体的BeanDefinitionParser来解析一个个element元素

  • 哪些是默认的命名空间,哪些是自定义的命名空间呢?是不是我们自己写的哪些xsd算是自定义呢?其实仅仅http://www.springframework.org/schema/beans该命名空间是默认命名空间,其他的均为自定义,诸如aop之流都属于自定义范畴,可以去看下spring的jar包中的spring.handler文件(x3框架没有该文件的原因是x3框架只有一个handler)

  • NamespaceHandler的理解:spring中的涵义为,管理一个命名空间下的所有parsers;但是X3中不一样,X3中自定义很多命名空间,但是只有一个hander,因此x3框架中的涵义可以理解为,管理所有拓展点的parsers。

BeanDefinitionParseDelegate(自定义命名空间的解析)

105634_PPQu_947581.jpeg

  • spring默认的NamespaceHanderResolver会读取meta/spring.handler,依据namespaceUri取得相应的hander

  • 生成解析所需要的上下文:ParserContext(持有BeanDefinitionRegister,解析完要注册的嘛)

  • X3框架就有很多parsers,都是继承的该类

BeanDefinitionDocumentReader(默认命名空间的解析)

105735_sGXU_947581.jpeg

  • 注册方法放在工具类中,因为很多地方会触发该注册行为

  • 装饰不知对应的何功能?

BeanDefinitionParserDelegate

105815_b4d3_947581.jpeg

  • 为何要包装一下,见注释

BeanDefinitionParserDelegate

105855_HRdo_947581.jpeg

  • BeanDefinition首次出现在了这个类中,标注下

BeanDefinitionDelegate

105930_MzcN_947581.jpeg

  • 举个解析bean节点的所有property属性来说明吧

BeanDefinition.parsePropertyValue(…)

110013_LIf3_947581.jpeg

解析map的例子

110045_rnFj_947581.jpeg

  • 返回的是个map,不知道getBean的时候是怎么处理的,待后续研究

  • BeanDefinition阶段的属性值的转化是在getBean的时候处理的,这个也值得研究,比如X3框架中ParserRequestContext对传递的参数做类型转换也用到了这个功能点,在学习getBean的时候深入下

涉及到的相关功能点

XML解析

由于时间比较仓促,就概要性的介绍下,主要是自己学习过程中的疑惑

  • 有图有真相,先来两张典型的图

112902_lX90_947581.jpeg

112945_2K9E_947581.jpeg

  • XML验证的时候需要一个验证文件,XSD或者DTD,但是需要一个PublicId和SystemId,网上的说法众说纷纭,参照spring的实现,我的理解是这样的:首先,PublicId和SystemId指的都是XML文件的外部引用,只不过前者大家的认知度更高一点,后者更加私有一点;其次,他们表达的都是验证文件的位置,在DTD中如果是PUBLIC的话需要指明详细地址(就是第二个参数),如果是SYSTEM的话就不需要用简称了,但是在XSD中并没有这个概念;最后,在代码中需要传入PublicId和SystemId,对于DTD都应该知道对应的是什么,但是XSD中Public为null,具体的url为SystemId

  • 验证文件填写的网络地址是不是每次验证都需要去下载呢?这就要看EntityResolver了,一般都是优先从本地加载,这是一个策略模式的应用

  • 验证XML都干了哪些事情:验证文档的合法性和填充一些默认的值。

  • 大致是如何解析XML的:主要有两种解析xml的方法,DOM和SAX,前者将xml整个载入内存,然后遍历每个节点,读取对应的验证文件进行验证;后者主要针对于xml过大的情况,它是事件驱动的方式来完成xml的解析,并且是分批次的读入XML来解析,因此存在无法修改的弊端

  • 解析XML和验证XML是如何交互的:首先,解析XML,当发现当前读取节点的命名空间的xsd没有加载的时候,加载该xsd,生成了一个dom语法,然后依据该语法验证该节点,当验证通过之后继续解析。

SpringExt相关

其实和springExt相关的实现已经嵌入到了前面了,这里就简单贴两张图,加深下理解

  • 流程图

113154_kZ7q_947581.jpeg

  • 主体实现类

113233_z2FW_947581.jpeg

113247_bmHG_947581.jpeg

  • X3框架是如何嵌入自己实现的XmlApplicationContext

113315_xnzi_947581.jpeg

113330_WDo4_947581.jpeg


转载于:https://my.oschina.net/tryUcatchUfinallyU/blog/266795

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值