暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

dubbo源码(一):xml标签解析

爱做菜的程序猿 2021-11-11
416

dubbo源码解析(一)

标签解析和bean的初始化

  • 首先找到程序入口,在dubbo-demo下的dubbo-demo-consumer中的consumer文件中:

进入查看代码发现依赖了一个配置类,META-INF/spring/dubbo-demo-consumer.xml。

    public static void main(String[] args) {
       //Prevent to get IPV6 address,this way only work in debug mode
       //But you can pass use -Djava.net.preferIPv4Stack=true,then it work well whether in debug mode or not
       System.setProperty("java.net.preferIPv4Stack", "true");
       ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
       context.start();
       DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy

       while (true) {
           try {
               Thread.sleep(1000);
               String hello = demoService.sayHello("world"); // call remote method
               System.out.println(hello); // get result

          } catch (Throwable throwable) {
               throwable.printStackTrace();
          }
      }
  }
  • 追踪META-INF/spring/dubbo-demo-consumer.xml这个配置类,我们发现它引用的xsd文件包括spring的spring-beans-4.3.xsd文件和dubbo.xsd,dubbo.xsd文件定义了xml文件的解析规则。例如看dubbo: reference,在xsd文件中可以看到reference对应的配置,对应ReferenceConfig类。**所以我们现在要追踪的是:

    • dubbo.xsd

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
      xmlns="http://www.springframework.org/schema/beans"
      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">

   <!-- consumer's application name, used for tracing dependency relationship (not a matching criterion),
   don't set it same as provider -->
   <dubbo:application name="dub-consumer"/>

   <!-- use multicast registry center to discover service -->
   <dubbo:registry address="multicast://224.5.6.7:1234"/>

   <!-- generate proxy for the remote service, then demoService can be used in the same way as the
   local regular interface -->
   <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
</beans>
  • ctrl+鼠标左键进入到dubbo.xsd,这里主要查看

    注:这里的<xsd:annotation>使用DubboBeanDefinitionParser方法转译成的bean,所以看不懂,往下看即可。

    • service

    • reference

    <xsd:element name="service" type="serviceType">
       <xsd:annotation>
           <xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
       </xsd:annotation>
   </xsd:element>

   <xsd:element name="reference" type="referenceType">
       <xsd:annotation>
           <xsd:documentation><![CDATA[ Reference service config ]]></xsd:documentation>
       </xsd:annotation>
   </xsd:element>
  • 找到dubbo.xsd的上级目录下的spring.handles:

    • 因为handles这里配置了DubboNamespaceHandler,进入这个类查看:可以发现这个类使用了DubboBeanDefinitionParser帮我们解析上面的标签,并且自定义了ServiceBean和ReferenceBean。

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

内部机制都是依托于<dubbo:annotation />标签。通过源码分析,Dubbo对于Spring xml解析处理由      com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler提供:DubboNamespaceHandler.java

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

   static {
       Version.checkDuplicate(DubboNamespaceHandler.class);
  }
   @Override
   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 AnnotationBeanDefinitionParser());
  }
}
  • 这里不急着看DubboBeanDefinitionParser,我们先来看一下registerBeanDefinitionParser这个类所需的元素,可以得到两个信息

    从这里我们可以看出DubboBeanDefinitionParser最终是返回了一个BeanDefinitionParser的类或者子类出来:

    • elementName:传入元素名

    • BeanDefinitionParser:传入解析后的bean

    protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
       this.parsers.put(elementName, parser);
  }
  • 点击进入DubboBeanDefinitionParser,可以发现:

    • element:表示 HTML 或 XML 文档中的元素。元素可能有与之关联的属性

    • beanclass:要被解析成的配置类(或者叫做,空的实体类)

    • required:是否进行解析

    • beanclass:要被解析成的配置类(或者叫做,空的实体类)

    • required:是否进行解析

    • DubboBeanDefinitionParser是一个构造函数,会根据传入的beanclass和required

    • pares方法调用了内部方法parse,介绍一下传入参数:

    public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
       this.beanClass = beanClass;
       this.required = required;
  }
   public BeanDefinition parse(Element element, ParserContext parserContext) {
       return parse(element, parserContext, beanClass, required);
  }
  • 下面跟进parse方法,查看发生了什么:

    • element:表示 HTML 或 XML 文档中的元素。元素可能有与之关联的属性

    • parserContext:解析外部命名空间的bean,所需要用到的类。

    • beanClass:要被解析成的配置类(或者叫做,空的实体类)

    • required:如果id为空,则进行解析,解析成xml里的name、interface、或者bean的名字

    • 解释一下传入参数

    @SuppressWarnings("unchecked")
   private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
       //创建一个RootBeanDefinition,作为要返回的类
       RootBeanDefinition beanDefinition = new RootBeanDefinition();
       //把我们需要注入的beanclass放入
       beanDefinition.setBeanClass(beanClass);
       //不设置懒加载
       beanDefinition.setLazyInit(false);
       //从xml里面获取id
       String id = element.getAttribute("id");//(*)
       //未获取到id或id为空,且是否需要解析为需要
       if ((id == null || id.length() == 0) && required) {
           //进行id的解析,根据结点属性解析成xml的标签属性或处理结点名#
           //从xml里获取bean的name
           String generatedBeanName = element.getAttribute("name");
           if (generatedBeanName == null || generatedBeanName.length() == 0) {
               //若为protocol处理结点,且name为空,则默认为 "dubbo"
               if (ProtocolConfig.class.equals(beanClass)) {
                   generatedBeanName = "dubbo";
              } else {
               //如果是普通结点则为其配置成interface的值
                   generatedBeanName = element.getAttribute("interface");
              }
          }
           if (generatedBeanName == null || generatedBeanName.length() == 0) {
               //如果id不为空,但generatedBeanName为空,则设置成对应的class名字
               generatedBeanName = beanClass.getName();
          }
           id = generatedBeanName;//将id改为置入的bean名(*)
           int counter = 2; //初始化counter为2
           while (parserContext.getRegistry().containsBeanDefinition(id)) {
               //为防止出现id重复,从parserContext中获取bean
               // 判断是否重复,重复则将generatedBeanName和counter进行拼串
               //重复则进行循环拼串,拼到不重复为止
               id = generatedBeanName + (counter++);//进行一个拼串(*)
          }
      }
       if (id != null && id.length() > 0) {//将id写入容器中
           //由于传入的required为true,所以这个位置必定会进入#
           //判断传入的id是否包含在parserContext中
           //包含则抛出异常
           if (parserContext.getRegistry().containsBeanDefinition(id)) {
               throw new IllegalStateException("Duplicate spring bean id " + id);
          }
           //注册id-beandefinetion到容器中
           parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
           //向我们要返回的beanDefinition传入id,前面一切的目的都是为了这一步,向里面放入id(*)
           beanDefinition.getPropertyValues().addPropertyValue("id", id);
      }
       // <dubbo:protocol/>标签处理
       if (ProtocolConfig.class.equals(beanClass)) { //如果是为protocol处理结点
           for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {//迭代获取bean的名字
               BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);//从拿到的名字里获取bean
               PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");//获取protocol处理结点
               if (property != null) {//如果结点不为空
                   Object value = property.getValue();//从处理结点中获取对象
                   if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                       //如果value是ProtocolConfig的实体类或者子类,并且id的值和ProtocolConfig的妹子相同
                       //获取知识点:instanceof,表示左边的是否为右边的实体类或子类
                       //则为其添加对当前bean id的依赖
                       definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                  }
              }
          }
           // <dubbo:service/>标签
      } else if (ServiceBean.class.equals(beanClass)) {//如果是service
           //从标签中获取bean
           String className = element.getAttribute("class");
           //如果存在这个bean
           if (className != null && className.length() > 0) {
               // 构建配置的class的BeanDefinition
               RootBeanDefinition classDefinition = new RootBeanDefinition();
               // 设置beanClass
               classDefinition.setBeanClass(ReflectUtils.forName(className));
               //不设置懒加载
               classDefinition.setLazyInit(false);
               //解析<property/>子标签
               parseProperties(element.getChildNodes(), classDefinition);
               //添加ServiceBean ref属性的依赖
               beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
          }
           // <dubbo:provider/>标签
      } else if (ProviderConfig.class.equals(beanClass)) {
           //解析嵌套元素,parseNested方法后面会讲。
           parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
           // <dubbo:consumer/>标签
      } else if (ConsumerConfig.class.equals(beanClass)) {
           //解析嵌套元素
           parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
      }
       Set<String> props = new HashSet<String>();//初始化一个hashset名为props
       ManagedMap parameters = null;//初始化一个Managedmap
       for (Method setter : beanClass.getMethods()) {//遍历beanclass的方法
           String name = setter.getName();//获取方法名
           //判断是否是public的有参数的setter方法
           if (name.length() > 3 && name.startsWith("set")
                   && Modifier.isPublic(setter.getModifiers())
                   && setter.getParameterTypes().length == 1) {
               //获取参数类型
               Class<?> type = setter.getParameterTypes()[0];
               // 将驼峰命名去除改成由-连接,如setApplicationContext --> application-context
               String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
               //加入我们定义的hashset中
               props.add(property);

               Method getter = null;//初始化一个方法命名为getter方法
               try {
                   //获取以get开头的方法
                   getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
              } catch (NoSuchMethodException e) { //捕获没有这个方法的异常
                   try {
                       //获取以is开头的方法
                       getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                  } catch (NoSuchMethodException e2) {//捕获没有这个方法的异常
                  }
              }
               //如果获取方法为空,或者方法不为公共,又或者setter方法的类型和getter方法返回类型不同,则退出循环
               // 注:type是setter方法的传入参数类型
               if (getter == null
                       || !Modifier.isPublic(getter.getModifiers())
                       || !type.equals(getter.getReturnType())) {
                   continue;
              }
               if ("parameters".equals(property)) {
                   /* parameters属性解析 */
                   parameters = parseParameters(element.getChildNodes(), beanDefinition);
              } else if ("methods".equals(property)) {
                   /* methods属性解析 */
                   parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
              } else if ("arguments".equals(property)) {
                   /* arguments属性解析 */
                   parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
              } else {
                   //如果都不是,则从标签里获取标签名为property的值
                   String value = element.getAttribute(property);
                   if (value != null) {
                   //值不为空,则去除值的头部和尾部的空字符
                       value = value.trim();
                       if (value.length() > 0) { //如果字符长度大于1
                           //如果property的值是registry,并且忽略大小写它的值是为N/A
                           if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
                               RegistryConfig registryConfig = new RegistryConfig();
                               //创建registryConfig,将其地址设置为N/A
                               registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
                               //将registryConfig设置进要返回的beanDefinition中
                               beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
                               //如果property不为registry,且不含有','
                          } else if ("registry".equals(property) && value.indexOf(',') != -1) {
                               //遍历设置成registries,也可以跟进查看一下parseMultiRef方法
                               parseMultiRef("registries", value, beanDefinition, parserContext);
                          } else if ("provider".equals(property) && value.indexOf(',') != -1) {
                               //遍历设置成providers
                               parseMultiRef("providers", value, beanDefinition, parserContext);
                          } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
                               //遍历设置成protocols
                               parseMultiRef("protocols", value, beanDefinition, parserContext);
                          } else {
                               Object reference;
                               //判断是否为基本数据类型和包装类型,类似Long,String,Short等
                               //以下的代码全部都是为了版本兼容,看到这里就差不多了,当然也可以继续看
                               if (isPrimitive(type)) {
                                   // 向后兼容旧版本的xsd中的默认值,因为每个版本的xsd都配有默认值,例如
                                   // <xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">
                                   if ("async".equals(property) && "false".equals(value)
                                           || "timeout".equals(property) && "0".equals(value)
                                           || "delay".equals(property) && "0".equals(value)
                                           || "version".equals(property) && "0.0.0".equals(value)
                                           || "stat".equals(property) && "-1".equals(value)
                                           || "reliable".equals(property) && "false".equals(value)) {
                                       // backward compatibility for the default value in old version's xsd
                                       value = null;
                                  }
                                   reference = value;
                              } else if ("protocol".equals(property)
                                       //如果属性为 protocol 那么要判断protocol对应的拓展点配置有没有
                                       && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
                                       //满足当前值没有在容器内 或者 在容器内类的名字不同
                                       && (!parserContext.getRegistry().containsBeanDefinition(value)
                                       || !ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
                                   if ("dubbo:provider".equals(element.getTagName())) {
                                       //标签的值如果等于dubbo:provider,则输出警告
                                       logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
                                  }

                                   ProtocolConfig protocol = new ProtocolConfig();
                                   protocol.setName(value);
                                   // 兼容旧版本配置
                                   reference = protocol;
                              } else if ("onreturn".equals(property)) {
                                   //回调方法 类似onSuccess
                                   int index = value.lastIndexOf(".");
                                   // bean的名字
                                   String returnRef = value.substring(0, index);
                                   String returnMethod = value.substring(index + 1);
                                   reference = new RuntimeBeanReference(returnRef);
                                   // 添加onreturnMethod属性值
                                   beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
                              } else if ("onthrow".equals(property)) {
                                   //回调 异常执行的方法 ,类似 onError
                                   int index = value.lastIndexOf(".");
                                   String throwRef = value.substring(0, index);
                                   String throwMethod = value.substring(index + 1);
                                   //获取ref和方法,将ref放入RuntimeBeanReference中
                                   reference = new RuntimeBeanReference(throwRef);
                                   //向我们自定义类里添加onthrowMethod的值
                                   beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
                              } else if ("oninvoke".equals(property)) {
                                   int index = value.lastIndexOf(".");
                                   String invokeRef = value.substring(0, index);
                                   String invokeRefMethod = value.substring(index + 1);
                                   //获取ref和方法,将ref放入RuntimeBeanReference中
                                   reference = new RuntimeBeanReference(invokeRef);
                                   //向我们自定义类里添加oninvokeMethod的值
                                   beanDefinition.getPropertyValues().addPropertyValue("oninvokeMethod", invokeRefMethod);
                              }else {
                                   //如果以上都没进行操作,并且property的字符等于ref,容器包含它的值,
                                   if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
                                       //从ref里面获取这个bean
                                       BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                                       /*
                                           必须是单例bean(singleton),原型bean(prototype)不行,sevice初始化一次,在spring容器里也只有一个 实例
                                           是不是和dubbo的幂等有关,如果为原型bean,那么服务就变成有状态的了
                                        */
                                       if (!refBean.isSingleton()) {//如果不是单例的bean
                                           throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
                                      }
                                  }
                                   reference = new RuntimeBeanReference(value);
                              }
                                /*
                                   设置属性,值为另外一个关联的bean
                                   RuntimeBeanReference 固定占位符类,当在beanfactory中作为另外一个bean的引用时,作为属性值对象,将在运行时进行解析
                                */
                               beanDefinition.getPropertyValues().addPropertyValue(property, reference);
                          }
                      }
                  }
              }
          }
      }
       NamedNodeMap attributes = element.getAttributes();
       //获取解析完后的下一个元素,就是还没被解析的元素
       //没见过这样的map,试了一下里面存储类Node,Node是一个包含很多信息的结点。
       int len = attributes.getLength();
       //遍历获取attributes
       for (int i = 0; i < len; i++) {
           //获取第i个索引对应节点
           Node node = attributes.item(i);
           //获取此结点的名字
           String name = node.getLocalName();
           if (!props.contains(name)) {//查看props否包含name
               if (parameters == null) {//如果parameters为空,则给与新的ManagedMap
                   parameters = new ManagedMap();
              }
               String value = node.getNodeValue();//获取node的值
               //向parameters放入结点名字,将value转成String类型
               parameters.put(name, new TypedStringValue(value, String.class));
          }
      }
       //如果parameters不为空
       if (parameters != null) {
           //将获取到的元素全部添加进自定义的bean中
           beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
      }
       //返回自定义的bean
       return beanDefinition;
  }
  • 下面讲一下parse里面用到的方法,一些简单的就略过了:

    private static void parseNested(Element element, ParserContext parserContext, Class<?> beanClass, boolean required, String tag, String property, String ref, BeanDefinition beanDefinition) {
       //获取标签的子节点
       NodeList nodeList = element.getChildNodes();
       //链表结点不为空
       if (nodeList != null && nodeList.getLength() > 0) {
           //初始化first为真
           boolean first = true;
           for (int i = 0; i < nodeList.getLength(); i++) {
               //传入索引i获取对应的结点node
               Node node = nodeList.item(i);
               //如果node和Element同类型或子类
               if (node instanceof Element) {
                    //传入的标签名和结点名相同 或者 和类名相同
                   if (tag.equals(node.getNodeName())
                           || tag.equals(node.getLocalName())) {
                       if (first) {
                           //把first设置为false
                           first = false;
                           String isDefault = element.getAttribute("default");
                           // 如果第一个子节点default属性为null,则设置为false
                           if (isDefault == null || isDefault.length() == 0) {
                               beanDefinition.getPropertyValues().addPropertyValue("default", "false");
                          }
                      }
                       // 递归解析嵌套的子节点
                       BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);
                       if (subDefinition != null && ref != null && ref.length() > 0) {
                           subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
                      }
                  }
              }
          }
      }
  }
//遍历nodeList,解析出对应的方法,放入我们自定义的bean里
   @SuppressWarnings("unchecked")
   private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
                                    ParserContext parserContext)
       
// 遍历元素,将元素存入map中,返回这个map
   @SuppressWarnings("unchecked")
   private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition)

    至此dubbo源码分析(一):xml标签解析已经完成,下一篇讲bean的生成,又是一个长流程。


文章转载自爱做菜的程序猿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论