spring 源码探索--xml的默认标签解析

开始对spring framwork explore,以下以及后续相关的文章都是自己一边看赫佳同志的《Spring 源码深度解析》一边做的笔记,比较潦草,有不对的地方希望多多指教。

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

这行代码完成了applicationContext.xml的解析和注册
大致流程:

  1. 使用Resource接口实现类和InputResource来封装资源文件
  2. 使用DefaultDocumentLoader将inputSource资源转换成Document
  3. 使用DefaultBeanDefinitionDocumentReader注册beanDefinition.

ClassPathResource Resource接口
封装配置文件

XmlBeanFactory
构造函数调用父类(AbstractAutowireCapableBeanFactory)构造函数

AbstractAutowireCapableBeanFactory
ignoreDependencyInterface自动装配时忽略给定的依赖接口,BeanNameAware.class,BeanFactoryAware.class

例如ApplicationContext通过ApplicationContextAware进行注入

为啥要忽略这些依赖接口?
例如:实现了ApplicationContextAware接口的类,有一个setApplicationContext方法,这个方法是spring容器执行的,这样bean可以直接访问spring容器

this.reader.loadBeanDefinitions(resource);
XmlBeanDefinitionReader
1. EncodedResource 封装资源
2. ThreadLocal<Set<EncodedResource>>设置当前的加载资源

doLoadBeanDefinitions(inputSource, encodedResource.getResource());

核心在这里:
1. loadDocument Document doc = doLoadDocument(inputSource, resource);
1.1. getEntityResolver.在项目中,寻找DTD声明,验证文档有效性。
使用EntityResolver的目的是不去网上下载对应的XSD/DTD文档,而是本地默认存在的,提高性能。
public InputSource resolveEntity(String publicId, String systemId)
通过publicId和systemId来寻找,systemId对应的就是beans.xml中声明部分xsi:schemaLocation中的第二部分(XML Schema文件位置)。在META-INF/spring.schemas文件中存在网络XML Schema文件位置和本地XML Schema文件位置的映射。
1.2. getValidationModeForResource 获取验证模式DTD OR XSD

DefaultDocumentLoader
借助java原生组件解析并得到document
1. 通过DocumentBuilderFactory获取DocumentBuilder
2. 解析InputSource(A single input source for an XML entity.)

  1. registerBeanDefinitions return registerBeanDefinitions(doc, resource);

documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
DefaultBeanDefinitionDocumentReader
doRegisterBeanDefinitions(Element root)

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)) {
            return;
        }
    }
}

preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);

this.delegate = parent;

parseBeanDefinitions(root, this.delegate); 中判断是默认的bean定义还是自定义的。
<bean id="test" class="test.TestBean" />
<tx:annotation-driven />

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
}

默认标签的解析

processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

BeanDefinitionParserDelegate

parseBeanDefinitionElement解析下面的所有字节

parseBeanDefinitionAttributes 解析字节的属性

BeanDefinition 是配置文件元素标签在在容器中的内部表示形式。

然后spring把BeanDefinition注册到BeanDefinitionRegistry中。

BeanDefinitionRegistry就是Spring配置信息的内存数据库,以map形式保存。

lookup-method 作用是把一个方法声明为返回某种类型的bean

parseConstructorArgElements(ele, bd);代码为例

遍历获取<constructor-arg ../>节点的index type name属性,解析当前constructor-arg节点下的构造函数的参数property,

  1. 有index的情况,property封装在ConstructorArgumentValues.ValueHolder valueHolder中,设置type name。把valueHolder与index一一映射保存起来

  2. 无index的情况.和上面的类似,区别是保存valueHolder时没有index.

那如何解析constructor-arg节点下属性呢?

  1. ref类型。找到ref节点,封装于RuntimeBeanReference中,设置source属性。
  2. value类型,封装于TypedStringValue,设置source属性。

一般节点被封装于一个valueHolder中,节点作为XmlReaderContext的extractSource(Element ele)的参数,保存在valueHolder的source中。

  1. 使用Resource接口实现类和InputResource来封装资源文件
  2. 使用DefaultDocumentLoader将inputSource资源转换成Document
  3. 使用DefaultBeanDefinitionDocumentReader注册beanDefinition.

对整体的解析流程再进行扩展一下

1.1 ClassPathResource 实现AbstractResource 典型的策略模式

2.1 getEntityResolver 作用是在当前项目下找到DTD的声明(不使用网络),然后验证文档的正确性

2.2 获得文档的验证模式,DTD OR XSD

2.3 spring 没有亲力亲为,使用javax.xml

3.1 利用BeanDefinitionParserDelegate解析bean(属性,constructor-arg,lookup等节点) import alias 等节点
3.2 利用BeanDefinitionParserDelegate解析默认标签中的自定义标签
3.3 使用BeanDefinitionReaderUtils注册BeanDefinition,byName,byAlias两种方式,其实就是把beandefinition放在map里面
3.4 通知监听器解析和注册过程完成 fireComponentRegistered

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值