一、环境准备

  在文件读取的时候,第9步我们发现spring会根据标签的namespace来选择读取方式,联想spring里提供的各种标签,比如<aop:xxx>等应该会有不同的读取和解析方式,这一章我们来找一个其他文件,了解下spring自定义标签和配置的读取流程。

  手边正好有一套dubbo的源码,因此为了区别与spring的原生读取,就使用它来进行分析。

  首先spring的配置文件中我们需要加上标签的namespace

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!--dubbo的命名空间-->
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
<!--dubbo的解析文件-->
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
"> <dubbo:application name="xixi_provider" /> </beans>

二、源码分析

1、我们回到上次的第9步

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = ; i < nl.getLength(); i++) {
Node node = nl.item(i);//[dubbo:application: null]
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);//namespace不是默认的,此处为入口
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

2、得到标签对应的namespace,

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);//http://code.alibabatech.com/schema/dubbo
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);//获取NamespaceHandlerResolver,并根据namespace找到对应的handler
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

3、获取NamespaceHandlerResolver的流程如下:

a)找到所有的配置文件META-INF/spring.handlers,并读取,将他们转化为handlerMappings

    private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}

b)从handlerMapping中获取到标签对应的handler名称,此处为:com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

我们可以到dubbo项目中查找,发现spring.handlers内容为:

  http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

与代码一致

c)利用反射,将类进行初始化,并保存在handlerMapping,下次可以直接使用

d)调用初始化init方法,我们进入init方法,看到应该是针对每个标签做了一个BeanDefinitionParser方法进行注入到了系统中

public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
}

4、根据标签的localName,获得解析标签的BeanDefinitionParser

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}

5、调用parser方法,因为dubbo的程序根据注入的beanClass和required进行解析,自己写程序时候,可以每个标签可以使用独立的parser类

    public BeanDefinition parse(Element element, ParserContext parserContext) {
return parse(element, parserContext, beanClass, required);
}

6、进入parser方法后,初始化beanDefinition,设置默认值

        RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);

7、如果没有且必须处理id,并注册

            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);

8、获取所有的字段ApplicationConfig,从节点中读取是否有值,如果有值进行注入

三、总结

看了dubbo的配置文件解析后,我们基本可以得出结论,spring中新增一种标签需要做以下工作:

1、定义namespace

2、编写NamespaceHandler并写入spring.handlers,指定namesapce对应的关系

3、在parser的init方法中,针对每种标签指定解析类DubboBeanDefinitionParser

定义完以上步骤后,spring会自动调用解析,解析的结果与默认的bean一致,均为BeanDefinition,所有标签自定义属性均解析到pv中,也就是自定义标签定义标签的外观,并未改变spring对bean的解析结果和ioc中实例化过程。

[spring源码学习]三、IOC源码——自定义配置文件读取的更多相关文章

  1. spring cloud深入学习(四)-----eureka源码解析、ribbon解析、声明式调用feign

    基本概念 1.Registe 一一服务注册当eureka Client向Eureka Server注册时,Eureka Client提供自身的元数据,比如IP地址.端口.运行状况指标的Uri.主页地址 ...

  2. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  3. Spring Boot 项目学习 (三) Spring Boot + Redis 搭建

    0 引言 本文主要介绍 Spring Boot 中 Redis 的配置和基本使用. 1 配置 Redis 1. 修改pom.xml,添加Redis依赖 <!-- Spring Boot Redi ...

  4. Spring源码学习之IOC容器实现原理(一)-DefaultListableBeanFactory

    从这个继承体系结构图来看,我们可以发现DefaultListableBeanFactory是第一个非抽象类,非接口类.实际IOC容器.所以这篇博客以DefaultListableBeanFactory ...

  5. spring源码学习(三)--spring循环引用源码学习

    在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出我的代码示例 @Component public class Add ...

  6. 5.2 spring5源码--spring AOP源码分析三---切面源码分析

    一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...

  7. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)浅谈 REST 和 CRUD

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  8. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  9. mybatis源码学习(三)-一级缓存二级缓存

    本文主要是个人学习mybatis缓存的学习笔记,主要有以下几个知识点 1.一级缓存配置信息 2.一级缓存源码学习笔记 3.二级缓存配置信息 4.二级缓存源码 5.一级缓存.二级缓存总结 1.一级缓存配 ...

  10. Vue源码学习三 ———— Vue构造函数包装

    Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...

随机推荐

  1. xml解析技术

    本文总结Dom,sax解析,  使用Java作为工具解析xml文档. 1 Dom 综述:Dom解析xml通常也称为xmlDom (和htmlDom技术差不多),将xml文档封装成树,好处就是xml中的 ...

  2. 【转】iOS9适配

    Demo1_iOS9网络适配_改用更安全的HTTPS iOS9把所有的http请求都改为https了:iOS9系统发送的网络请求将统一使用TLS 1.2 SSL.采用TLS 1.2 协议,目的是 强制 ...

  3. JS心得——判断一个对象是否为空

    判断一个对象是否为空对象,本文给出三种判断方法: 最常见的思路,for...in...遍历属性,为真则为"非空数组":否则为"空数组" 2.通过JSON自带的. ...

  4. window.top.location.href 和 window.location.href 的区别

    "window.location.href"."location.href"是本页面跳转. "parent.location.href" 是 ...

  5. Android 学习资源收集

    1.2015最流行的Android组件.工具.框架大全 地址  http://www.open-open.com/lib/view/open1436262653692.html

  6. 多个.ui共用一个.qrc出错

    在一个已经组建完成的qt项目中,如果再加入新的界面文件,界面文件是无法直接使用原工程的.qrc文件的(执行添加资源操作时不显示资源文件),必须重启一次Qt. 版本: Qt 5.7.0 Qt Creat ...

  7. JSPatch热更新的利器.

    如果用一句话来描述JSPatch,就是利用系统自带的JavaScriptCore.framework配合RunTime机制,进行实时的代码下载与运行.. 而且使用也很简单,启动,加载JS,运行... ...

  8. Nagios

    什么是Nagios? Nagios是一款用于系统和网络监控的应用程序.它可以在你设定的条件下对主机和服务进行监控, 在状态变差和变好的时候给出告警信息. Nagios更进一步的特征包括: 1. 监控网 ...

  9. Eclipse ndk fix插件开发

    一. 手工修复ndk环境bug Eclipse做ndk开发的时候, 经常会遇到编译过去,却报语法错误的问题,比如 ①. 头文件不识别 ②. 头文件识别了, 类型不识别 针对这一的bug,我们一般按照如 ...

  10. Delaunay剖分与平面欧几里得距离最小生成树

    这个东西代码我是对着Trinkle的写的,所以就不放代码了.. Delaunay剖分的定义: 一个三角剖分是Delaunay的当且仅当其中的每个三角形的外接圆内部(不包括边界)都没有点. 它的存在性是 ...