博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
解析spring启动加载dubbo过程
阅读量:5925 次
发布时间:2019-06-19

本文共 5293 字,大约阅读时间需要 17 分钟。

一:简单配置

web.xml

contextConfigLocation
classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener

applicationContext.xml

dubbo的默认文件

spring.handlers文件http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandlerhttp\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandlerspring.schemas文件http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsdhttp\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd

说明: spring.handlers文件用来配置解析dubbo标签并封装成对应的对象

spring.schemas文件用来配置schame文件的位置

当spring容器扫描到配置文件,比如applicationContext时,遇到名称空间xmlns:dubbo="http://code.alibabatech.com/schema/dubbo",就会通过名称空间去查询对应的xsd约束文件,就如schemaLocation中配置的

  • xsi:schemaLocation=http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
  • 再用这个找到的xsd去spring.schemas文件中找到xsd文件的位置,并校验xsd文件的正确性,返回当前文件(applicationContext.xml)的Document对象

二、启动过程

加载dubbo属性时序图

  1. 由ContextLoaderListener进入,调用contextInitialized进入initWebApplicationContext开始spring容器

  2. 进入configureAndRefreshWebApplicationContext方法, 然后调用refresh方法

  3. refresh中有个obtainFreshBeanFactory方法,进去

  4. 走流水线AbstractApplicationContext->AbstractRefreshableApplicationContext#refreshBeanFactory -> XmlWebApplicationContext#loadBeanDefinitions

  5. 进入到XmlBeanDefinitionReader#loadBeanDefinitions(configLocation)

    // 遍历你contextConfigLocation配置的多个xml文件for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation);}
  6. 来到AbstractBeanDefinitionReader#loadBeanDefinitions-> XmlBeanDefinitionReader#loadBeanDefinitions, doLoadBeanDefinitions, registerBeanDefinitions

    看一下doLoadBeanDefinitions 这里会加载你的spring.schame文件

//这个方法会检查你的文件(eg. applicationContext.xml)中的schame的namespace对应的xsd是否正确,返回当前文件的Document对象Document doc = doLoadDocument(inputSource, resource);return registerBeanDefinitions(doc, resource);

这个方法是 注册给定DOM文档中包含的bean定义, 也就是解析你的applicationContext.xml中定义的bean

registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws... {    //创建新的doc解析器   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();   // 已有的bean个数   int countBefore = getRegistry().getBeanDefinitionCount();   // 读取document中的bean   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));   return getRegistry().getBeanDefinitionCount() - countBefore;}
  1. DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
@Overridepublic void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {    this.readerContext = readerContext;    Element root = doc.getDocumentElement();    //读取bean    doRegisterBeanDefinitions(root);}
  1. BeanDefinitionParserDelegate#parseCustomElement 这里就从容器中调用到自定义的handler中
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {        //获取节点的NamespaceURI, 如果当前节点是dubbo:application 那么就是根据dubbo找到      //xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 后面的uri        String namespaceUri = getNamespaceURI(ele);      //这里的resolve方法,会加载spring.handlers文件,调用namespaceHandler.init();获取当前节点对应的handler解析器        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);      //parse方法会先根据element的名称获取对应的BeanDefinitionParser      //比如当前元素是dubbo:application, 这里会用application作为key去获取对应的解析器,然后调用其parse方法        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));    }
  1. DubboBeanDefinitionParser#parse

关于第8步,加载了spring.handlers文件,然后调用DubboNamespaceHandler的init方法,然后是registerBeanDefinitionParser方法,该方法将节点名称和解析封装在NamespaceHandlerSupport的map中

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {        this.parsers.put(elementName, parser);    }

然后在handler.parse(...) -> NamespaceHandlerSupport#findParserForElement方法里, 能通过下面的方式获取到

BeanDefinitionParser parser = this.parsers.get(localName);

三、说一个遇到的坑-关于idea的this与toString

在查看解析spring.schames文件的源码时,走到PluggableSchemaResolver类里面,当断点进入resolveEntity方法时,属性private volatile Map<String, String> schemaMappings; 已经有值了,这是不正常的,为此我花了很长时间去找原因

​ 最后发现,是由于idea在debug时,在debugger界面有个this引用着当前对象,它会调用当前类的toString()方法,而且是构造方法执行后每执行一步,它刷新一次(调用toString()),由于toString方法中有调用getSchemaMappings方法,会给你加载数据。 最坑的是,idea调用的用断点拦截不到

this引起的烦恼

你可以尝试着还原这个坑

public class MyTest {    public static void main(String[] args) {        BB b = new BB();    }    static class BB {        private int n = 0;        public BB() {            System.out.println("i am qiao" + n);        }        @Override        public String toString() {            n = 2;            System.out.println("hello");            return  "n == " + n;        }    }}

四、小结:

  1. web.xml中 参数contextConfigLocation的值可以使用,; \t\n(逗号|分号|空格|制表符|换行)分隔开(ContextLoader.INIT_PARAM_DELIMITERS)
  2. classpath:与classpath*: 的区别

    classpath: 会从classes目录下获取文件

    classpath*: 会从所有路径下加载文件,包括jar包

    详见:PathMatchingResourcePatternResolver#getResources

  3. spring.handlers文件用来配置解析dubbo标签的handler

    spring.schemas文件用来配置schame文件的位置

    PluggableSchemaResolver类解析的spring.shames

    DefaultNamespaceHandlerResolver类解析的spring.handler

    ---恢复内容结束---

转载于:https://www.cnblogs.com/qiaozhuangshi/p/10831332.html

你可能感兴趣的文章
电影网站
查看>>
Windows Server 2012 R2 安装GUI
查看>>
RHEL7和RHEL6的版本对比
查看>>
Hadoop1的一些配置项
查看>>
我的友情链接
查看>>
RHEL5学习笔记——RH033(未完)
查看>>
mysql 中的存储过程,函数
查看>>
Python import
查看>>
JavaScript数组功能扩展--差集,并集,合集,去重
查看>>
web项目小总结
查看>>
你有没有聪明到利用云给你带来回报?
查看>>
我的友情链接
查看>>
分享有用的 Linux 命令行网络监控工具
查看>>
《.NET 编程结构》专题汇总
查看>>
MapReduce和spark的shuffle过程详解
查看>>
oracle修改MAX_STRING_SIZE,突然断电处理
查看>>
java hibernate关联查询返回一个集合,转换成json报json死循环处理
查看>>
C# 删除Collections中的重复数据
查看>>
NFS 网络系统配置及自动挂载
查看>>
corosync(openais)+drbd+pacemaker实现mysql服务器的高可用性群集
查看>>