这篇文章是Dubbo源码分析的开端,与其说这篇文章是Dubbo源码分析,不如是spring源码分析,因为大部分都是在分析spring如何解析xml配置文件的,为了与后面的Dubbo源码分析保持一致,姑且这样命名了。

使用Dubbo框架开发分布式服务时,一般使用spring进行管理,在spring的配置文件中进行配置,例如服务提供者Provider端配置如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
  6. <!-- 提供方应用信息,用于计算依赖关系 -->
  7. <dubbo:application name="hello-world-app" />
  8. <!-- 使用multicast广播注册中心暴露服务地址 -->
  9. <dubbo:registry address="multicast://224.5.6.7:1234" />
  10. <!-- 用dubbo协议在20880端口暴露服务 -->
  11. <dubbo:protocol name="dubbo" port="20880" />
  12. <!-- 声明需要暴露的服务接口 -->
  13. <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
  14. <!-- 和本地bean一样实现服务 -->
  15. <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
  16. </beans>

对于标签我们都熟悉了,这是spring提供给使用者实例化PoJo类的。那么dubbo定义的标签spring是如何识别的呢?

其实spring-beans jar包中提供了一个口,那就是NamespaceHandler,它的定义如下:

  1. (省略描述....)
  2. public interface NamespaceHandler {
  3. void init();
  4. BeanDefinition parse(Element element, ParserContext parserContext);
  5. BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
  6. }

可以看出其中有一个parse方法,它的作用就是用于解析Xml文档中的节点。这个接口有一个抽象的实现类NamespaceHandlerSupport,具体的标签解析器都继承这个抽象类,我们来看下有哪些:

可以看见我们熟悉的Aop还有Dubbo NamespaceHandler.那Spring是如何知道要使用Dubbo定义的handler来解析自定义的标签呢?他们的结合点就在一个配置文件,spring留了一个配置文件,只要我们配置了它,spring就可以找到。这个配置文件名字叫做spring.handlers,spring在解析xml文件时,会去加载spring.handlers配置文件,然后寻找能够解析自定义标签的handler。

spring.handlers在Dubbo中是怎样的内容呢,我们一起来看下:

路径在:dubbo-config/dubbon-config-spring/META-INF/spring.handlers

内容如下:

http://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler

http://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler

我们来看一下spring是如何找到这个文件并生成对应标签namespaceHandler的:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"application.xml"});

我们使用spring框架,启动的时候都会写这么一句话,加载xml配置文件.当执行到AbstractXmlApplicationContext类的loadBeanDefinitions方法时,会创建一个XmlBeanDefinitionReader对象,读取读取并解析xml.

  1. /**
  2. * Loads the bean definitions via an XmlBeanDefinitionReader.
  3. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
  4. * @see #initBeanDefinitionReader
  5. * @see #loadBeanDefinitions
  6. */
  7. @Override
  8. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  9. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  10. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  11. // Configure the bean definition reader with this context's
  12. // resource loading environment.
  13. beanDefinitionReader.setEnvironment(this.getEnvironment());
  14. beanDefinitionReader.setResourceLoader(this);
  15. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  16. // Allow a subclass to provide custom initialization of the reader,
  17. // then proceed with actually loading the bean definitions.
  18. initBeanDefinitionReader(beanDefinitionReader);
  19. loadBeanDefinitions(beanDefinitionReader);
  20. }

最后一句话loadBeanDefinitions(beanDefinitionReader),是使用XmlBeanDefinitionReader将xml解析成BeanDefinition.

接下来调用XmlBeanDefinitionReader的registerBeanDefinitions方法创建BeanDefinitionDocumentReader对象,这个对象才是真正解析XML的对象。

  1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  2. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  3. int countBefore = getRegistry().getBeanDefinitionCount();
  4. //创建ReaderContext,并调用documentReader的registerBeanDefinitions方法
  5. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  6. return getRegistry().getBeanDefinitionCount() - countBefore;
  7. }

大家再来看下createReaderContext方法

  1. /**
  2. * Create the {@link XmlReaderContext} to pass over to the document reader.
  3. */
  4. public XmlReaderContext createReaderContext(Resource resource) {
  5. return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
  6. this.sourceExtractor, this, getNamespaceHandlerResolver());
  7. }

终于出现了NamespaceHandler,那就是getNamespaceHandlerResolver()方法,再来看下这个方法

  1. public NamespaceHandlerResolver getNamespaceHandlerResolver() {
  2. if (this.namespaceHandlerResolver == null) {
  3. this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
  4. }
  5. return this.namespaceHandlerResolver;
  6. }
  7. protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
  8. return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
  9. }
  10. public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
  11. this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
  12. }
  13. //最终会调用这个构造器实例化DefaultNamespaceHandlerResolver类,此时handlerMappingsLocation成员变量的值为META-INF/spring.handlers了
  14. public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
  15. Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
  16. this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
  17. this.handlerMappingsLocation = handlerMappingsLocation;
  18. }
  19. /**
  20. * The location to look for the mapping files. Can be present in multiple JAR files.
  21. */
  22. public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
  23. //读取META-INF/spring.handlers配置文件的内容
  24. private Map<String, Object> getHandlerMappings() {
  25. if (this.handlerMappings == null) {
  26. synchronized (this) {
  27. if (this.handlerMappings == null) {
  28. try {
  29. Properties mappings =
  30. PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
  31. if (logger.isDebugEnabled()) {
  32. logger.debug("Loaded NamespaceHandler mappings: " + mappings);
  33. }
  34. Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
  35. CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
  36. this.handlerMappings = handlerMappings;
  37. }
  38. catch (IOException ex) {
  39. throw new IllegalStateException(
  40. "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
  41. }
  42. }
  43. }
  44. }
  45. return this.handlerMappings;
  46. }

会创建一个默认的DefaultNamespaceHandlerResolver对象,其中有一个变量DEFAULT_HANDLER_MAPPINGS_LOCATION,其值是META-INF/spring.handlers,此时可以看到spring会去加载spring.hadlers中的内容。

那加载完后,会在什么地方使用呢?

我们来看一下DefaultBeanDefinitionDocumentReader类中的parseBeanDefinitions方法

  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.isDefaultNamespace(root)) {
  3. NodeList nl = root.getChildNodes();
  4. for (int i = 0; i < nl.getLength(); i++) {
  5. Node node = nl.item(i);
  6. if (node instanceof Element) {
  7. Element ele = (Element) node;
  8. //判断标签是标准的标准还是自定义的,判断的依据就是标准的namespace是否是http://www.springframework.org/schema/beans
  9. //如果是,则是标准的标签,如果不是则不是标准的标签
  10. if (delegate.isDefaultNamespace(ele)) {
  11. parseDefaultElement(ele, delegate);
  12. }
  13. else {
  14. delegate.parseCustomElement(ele);
  15. }
  16. }
  17. }
  18. }
  19. else {
  20. delegate.parseCustomElement(root);
  21. }
  22. }

接下来看一下BeanDefinitionParserDelegate类的parseCustomElement方法,BeanDefinitionParserDelegate对象是在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法中生成的

,它的作用是用来解析标签的。

  1. public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
  2. //根据标签得到标签的namespace
  3. //dubbo自定义的标签得到的namespace是http://dubbo.apache.org/schema/dubbo
  4. String namespaceUri = getNamespaceURI(ele);
  5. NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  6. if (handler == null) {
  7. error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
  8. return null;
  9. }
  10. return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  11. }

接下来看下DefaultBeanDefinitionDocumentReader的resolve方法

  1. @Override
  2. public NamespaceHandler resolve(String namespaceUri) {
  3. //得到加载的内容
  4. Map<String, Object> handlerMappings = getHandlerMappings();
  5. //根据标签的namspace得到handler类名
  6. //dubbo的namespace为http://dubbo.apache.org/schema/dubbo
  7. Object handlerOrClassName = handlerMappings.get(namespaceUri);
  8. if (handlerOrClassName == null) {
  9. return null;
  10. }
  11. else if (handlerOrClassName instanceof NamespaceHandler) {
  12. return (NamespaceHandler) handlerOrClassName;
  13. }
  14. else {
  15. String className = (String) handlerOrClassName;
  16. try {
  17. Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
  18. if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
  19. throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
  20. "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
  21. }
  22. //实例化namespaceHandler
  23. NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
  24. namespaceHandler.init();
  25. handlerMappings.put(namespaceUri, namespaceHandler);
  26. return namespaceHandler;
  27. }
  28. catch (ClassNotFoundException ex) {
  29. throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
  30. namespaceUri + "] not found", ex);
  31. }
  32. catch (LinkageError err) {
  33. throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
  34. namespaceUri + "]: problem with handler class file or dependent class", err);
  35. }
  36. }
  37. }

得到DubboNamespaceHandler实例后,调用其init()方法

  1. @Override
  2. public void init() {
  3. //将application、module等名称做为key注册到解析器Map中
  4. //这些key名称正是dubbo自定义标签的localName,例如<dubbo:application />标签的LocalName是application
  5. registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
  6. registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
  7. registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
  8. registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
  9. registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
  10. registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
  11. registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
  12. registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
  13. registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
  14. registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
  15. }

可以看到所有的key(除了annotation)对应的标签解析器都是DubboBeanDefinitionParser实例对象,至于DubboBeanDefinitionParser是如何解析标签的,这里就不做分析了,如果想要了解,可以看看他的源码。

如果不做特别说明,此篇往后所有的Dubbo源码分析都是基于Dubbo2.7.0

Dubbo2.7源码分析-Dubbo是如何整合spring-framework的的更多相关文章

  1. Spring Security 源码分析(四):Spring Social实现微信社交登录

    社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...

  2. Dubbo2.7源码分析-如何发布服务

    Dubbo的服务发布逻辑是比较复杂的,我还是以Dubbo自带的示例讲解,这样更方便和容易理解. Provider配置如下: <?xml version="1.0" encod ...

  3. 源码分析Dubbo服务消费端启动流程

    通过前面文章详解,我们知道Dubbo服务消费者标签dubbo:reference最终会在Spring容器中创建一个对应的ReferenceBean实例,而ReferenceBean实现了Spring生 ...

  4. ABP源码分析二十七:ABP.Entity Framework

    IRepository:接口定义了Repository常见的方法 AbpRepositoryBase:实现了IRepository接口的常见方法 EfRepositoryBase:实现了AbpRepo ...

  5. MyBatis源码分析(六):Spring整合分析

    一.Mybatis-Spring源码结构 二.Myabtis交给Spring管理的组件 1. dataSource 数据源 配置一个数据源,只要是实现了javax.sql.DataSource接口就可 ...

  6. dubbo源码分析--dubbo spi解析

    1. 什么叫SPI? 简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射. 2. java SPI与Dubbo SPI有什么区别 (此图来自网上,我没有刻意去截图) 然后在这个文件 ...

  7. Dubbo2.7源码分析-SPI的应用

    SPI简介 SPI是Service Provider Interface的缩写,即服务提供接口(翻译出来好绕口,还是不翻译的好),实质上是接口,作用是对外提供服务. SPI是Java的一种插件机制,可 ...

  8. spring5源码分析系列(二)——spring核心容器体系结构

    首先我们来认识下IOC和DI: IOC(Inversion of Control)控制反转:控制反转,就是把原先代码里面需要实现的对象创建.依赖的代码,反转给容器来帮忙实现.所以需要创建一个容器,并且 ...

  9. 源码分析--dubbo服务端暴露

    服务暴露的入口方法是 ServiceBean 的 onApplicationEvent.onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务 ...

随机推荐

  1. Tmux入门教程

      对于程序员来说效率绝对是最重要的,那我们今天就来介绍下一个能极大提高工作效率的软件Tmux.   Tmux 是一个工具,用于在一个终端窗口中运行多个终端会话.不仅如此,你还可以通过 Tmux 使终 ...

  2. Leader Election 选举算法

    今天讲一讲分布式系统中必不可少的选举算法. leader 就是一堆服务器中的协调者,某一个时刻只能有一个leader且所有服务器都承认这个leader. leader election就是在一组进程中 ...

  3. 为某金融企业的IT技术部人员提供基于TFS的软件研发流程介绍

    受莫金融企业IT信息技术部的邀请,为该金融企业的某省分公司.地市分公司的IT技术人员提供了一场基于TFS的软件研发流程的技术培训,希望可以借此提高该企业的软件研发.运维水平,同时推动企业软件研发信息化 ...

  4. [翻译]NUnit---Property and Random Attributes(十四)

    小记:由于工作琐碎,没得心情翻译而且也在看<CLR vis C#>,所以断更了差不多5个月,现在继续翻译,保证会翻译完成,不会虎头蛇尾. 另:NUnit已经更新到2.6.3版本,虽然正在开 ...

  5. wpf(使用定时器)使用定时器操作UI界面

    在项目实践中,我们 可能会遇到需要将一些控件上显示的内容只显示一段时间过后清空. 下面我们来实现这种操作: 首先需要注意的是:在wpf中涉及到界面操作的话,一定要使用定时器DispatcherTime ...

  6. R语言和RStudio的一些用法,常用命令等

    控制台: Up/down 回忆之前的命令 Ctrl+Up 回顾命令列表(可先输入前缀进行查找) 焦点: ctrl+ 移动焦点到source编辑器 ctrl+ 移动焦点到console ctrl+L 清 ...

  7. 使用libxml2进行xml开发(一)

    (一)Windows下使用MinGW和Code::Blocks环境配置libxml2 笔者此次是在windows 7下使用MinGW和Code::Blocks开发C程式的,手上的一个项目需要使用soc ...

  8. Android与js交互

    本文转载自:http://blog.csdn.net/it1039871366/article/details/46372207 一文. Android 中可以通过webview来实现和js的交互,在 ...

  9. Django templates html中进行模板渲染时使用python语法的基本方式

    导包(可以在模板中导入python包进行使用): <%!import urllib%> <%! from ** import **%> 使用if for等python语句: % ...

  10. [Other] 应用下载网站的APK/IPA等常见MIME设置

    类型 扩展名 mime 错误做法 安卓安装包 .apk application/vnd.android.package-archive application/vnd.android 苹果安装包 .i ...