Spring源码解析(二)

这篇文章是接着《Spring源码解析(一)》写的,建议读者先去看看Spring源码解析(一),这样循序渐进收获会更好

访问地址https://blog.csdn.net/dachengzi159/article/details/81180656

获取XML的验证模式

了解XML文件的读者都应该知道XML文件的验证模式保证了XML文件的正确性,而比较常见的验证模式有两种:DTD和XSD。

DTD即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。
DTD 是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。 一个 DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。

DTD和XSD相比:DTD 是使用非 XML 语法编写的。
DTD 不可扩展,不支持命名空间,只提供非常有限的数据类型 .

XML Schema语言也就是XSD。XML Schema描述了XML文档的结构。
可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。
一个XML Schema会定义:文档中出现的元素、文档中出现的属性、子元素、子元素的数量、子元素的顺序、元素是否为空、元素和属性的数据类型、元素或属性的默认 和固定值。
XSD是DTD替代者的原因,一是据将来的条件可扩展,二是比DTD丰富和有用,三是用XML书写,四是支持数据类型,五是支持命名空间。

了解了DTD与XSD的区别后我们再去分析Spring中对于验证模式的提取就更容易理解了。通过对XmlBeanDefinitionReader类的解读,锁定Spring通过getValidationModeForResource方法获取对应资源的验证模式。

如果手动指定了验证模式则使用指定的验证模式

Spring源码解析(二)_第1张图片

如果没有指定就使用自动检测

 Spring源码解析(二)_第2张图片

 方法的实现其实还是很简单的,无非是如果设定了验证模式则使用设定的验证模式(可以通过对调用XmlBeanDefinitionReader中的setValidationMode方法进行设定),否则使用自动检测的方式。而自动检测验证模式的功能是在函数detectValidationMode方法中实现的,在detectValidationMode函数中又将自动检测的功能委托给专门处理类validationModeDetector,调用detectValidationMode方法,具体代码如下:

Spring源码解析(二)_第3张图片

 理解上面的代码应该不会太难,Spring用来检测验证模式的方法就是判断是否包含DOCTYPE

,如果包含就是DTD,如果不包含就是XSD。

获取Document

经过了验证模式准备的步骤就可以进行Document加载了,同样XmlBeanDefinitionReader类对于文档读取并没有亲力亲为,而是委托了DocumentLoader去执行,这里的DocumentLoader是个接口,而真正调用的是DefaultDocumentLoader。

 Spring源码解析(二)_第4张图片

 Spring源码解析(二)_第5张图片

 通过SAX解析XML文档的套路大致都差不多,首先创建createDocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource来返回Document对象。这里有必要提及一下EntityResolver,对于参数entityResolver,传入的是通过getEntityResolver()函数获取的返回值。

Spring源码解析(二)_第6张图片

 解析并注册BeanDefinitions

 当把文件转化为Document后,接下来的提取及注册bean就是我们的重头戏了

Spring源码解析(二)_第7张图片

 其中的参数是通过上面的loadDocument加载转化出来的。在这个方法中很好的应用了面向对象中单一职责的原则,将逻辑处理委托给单一的类进行处理,这个逻辑处理类就是BeanDefinitionDocumentReader。BeanDefinitionDocumentReader是一个接口,实例化工作是在createBeanDefinitionDocumentReader()中完成的,而通过此方法,BeanDefinitionDocumentReader真正的类型其实已经是DefaultBeanDefinitionDocumentReader了,进入DefaultBeanDefinitionDocumentReader后,发现这个方法的重要目的就是提取root,以便再次将root作为参数继续BeanDefinition的注册。(Spring的命名就像我开始说的那样,就像讲故事一样,老是记不住)。

 Spring源码解析(二)_第8张图片

终于到了核心逻辑的底层doRegisterBeanDefinitions(root),doRegisterBeanDefinitions(root)开始真正地进行解析了,我们期待的核心部分真正开始了。

Spring源码解析(二)_第9张图片

 

重点我已经勾画出来了。

首先是对profile的处理,然后开始解析,可是当我们跟进preProcessXml(root)和postProcessXml(root)发现代码是空的,既然是空的还有什么用呢?我是这样理解的。一个类要么是面向继承而设计的,要么就是用final修饰的。在DefaultBeanDefinitionDocumentReader中并没有用final修饰,所以它是面向继承而设计的。那么我就不卖关子了,这是模板方法模式,如果继承自DefaultBeanDefinitionDocumentReader的子类需要在bean解析前后做一些处理的话,那么只需重写这两个方法就可以了。

profile属性的使用

这个属性没有用过,不知道怎么说,我查找了相关资料,大概总结一下吧:

首先程序会获取beans节点是否定义了profile属性,如果定义了就到环境变量中去查找,所以需要首先确定environment不可能为空,因为profile是可以同时指定多个的,需要程序对其拆分,并解析每个profile是都符合环境变量中所定义的,不定义则不会浪费性能去解析。

解析并注册BeanDefinition

处理了profile后就可以进行XML的读取了,跟踪代码进入parseBeanDefinitions(root,this.delegate)。

 Spring源码解析(二)_第10张图片

 

上面的代码看起来逻辑还是蛮清楚的,因为在Spring的XML配置里面有两大类Bean声明,一个是默认的,如:

<bean id="myTestBean" class="test.hechen.spring.MyTestBean"/>

另一类就是自定义的:

<tx:annotation-driven>

而这两种方式的读取及解析差别还是非常大的,如果采用默认的配置,Spring当然知道该怎么做,但是如果是自定义的,那么就需要用户实现一些接口及配置了。对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement方法对自定义命名空间进行解析。而判断是否默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI()获取命名空间,并与Spring中固定的命名空间http://www.Springframework.org/schema/beans进行比对。如果一致则认为是默认,否则就是自定义。bean的加载这一块很难用一个小分类去讲解,我将用另一个分类去解析。

码字不易,如需转载请标明出处。 

 

 

 

你可能感兴趣的:(Spring源码系列)