Dubbo之DubboBeanDefinitionParser源码解析

功能概述

  • Dubbo框架会集成Spring的能力,在以XML方式配置信息时,会用自定义的处理器DubboNamespaceHandler和解析器DubboBeanDefinitionParser对XML进行解析,生成对应的Bean对象和Config对象。

功能分析

DubboBeanDefinitionParser类分析

主要成员变量分析

java 复制代码
private final Class<?> beanClass; // Bean对应的Class类(即Config的Class类)
private final boolean required;   // 是否必须(即Config类的id是否必须)

主要成员方法分析

解析XML元素

java 复制代码
private static RootBeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { //ParserContext:通过bean定义解析过程传递的上下文,封装所有相关配置和状态,嵌套在XmlReaderContext内
    RootBeanDefinition beanDefinition = new RootBeanDefinition(); //创建spring的Bean实例
    beanDefinition.setBeanClass(beanClass); //设置Bean对应的class类,如MethodConfig.class
    beanDefinition.setLazyInit(false); //设置是否延迟初始化,false:在spring容器启动时,就会创建Bean实例
    String id = resolveAttribute(element, "id", parserContext); //解析属性id的值
    /**
     * 处理属性id的值,若属性id为空且是必须的,则尝试获取name、interface属性对应的值,若还为空则获取bean的名称
     */
    if (StringUtils.isEmpty(id) && required) { //未设置属性id时,使用其它属性产生id值
        String generatedBeanName = resolveAttribute(element, "name", parserContext); //解析属性name的值
        if (StringUtils.isEmpty(generatedBeanName)) {
            if (ProtocolConfig.class.equals(beanClass)) {
                generatedBeanName = "dubbo";
            } else {
                generatedBeanName = resolveAttribute(element, "interface", parserContext);
            }
        }
        if (StringUtils.isEmpty(generatedBeanName)) {
            generatedBeanName = beanClass.getName(); //bean的名称如:class org.apache.dubbo.config.ApplicationConfig
        }
        id = generatedBeanName;
        int counter = 2;
        while (parserContext.getRegistry().containsBeanDefinition(id)) { //若bean已存在,则加上计数标识,直到没有重复的id为止
            id = generatedBeanName + (counter++); // count++的值从2开始递增,变量count在前,count++就是加之前的值
        }
    }
    if (StringUtils.isNotEmpty(id)) {
        if (parserContext.getRegistry().containsBeanDefinition(id)) { //判断已经注册的Bean中是否有重复的id
            throw new IllegalStateException("Duplicate spring bean id " + id);
        }
        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); // 向spring注册中心注册bean实例(将id作为Bean的名称)
        beanDefinition.getPropertyValues().addPropertyValue("id", id); // 设置bean的id属性
    }
    /**
     * 对指定的bean进行处理,如ProtocolConfig、ServiceBean、ProviderConfig、ConsumerConfig等
     */
    if (ProtocolConfig.class.equals(beanClass)) {
        for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { //遍历已注册的bean对应的名称列表
            BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); //获取指定bean名称对应的BeanDefinition实例
            PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
            if (property != null) {
                Object value = property.getValue();
                if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                    definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                }
            }
        }
    } else if (ServiceBean.class.equals(beanClass)) {
        String className = resolveAttribute(element, "class", parserContext);
        if (StringUtils.isNotEmpty(className)) { //在<dubbo:service/>中设置class属性时进入
            RootBeanDefinition classDefinition = new RootBeanDefinition(); //根bean定义也可以用于注册单个bean定义
            classDefinition.setBeanClass(ReflectUtils.forName(className));
            classDefinition.setLazyInit(false);
            parseProperties(element.getChildNodes(), classDefinition, parserContext);
            beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); //BeanDefinitionHolder:带有名称和别名的bean定义的Holder
            // 此处的bean实例,会在bean的名称后面加上Impl,如:org.apache.dubbo.demo.DemoService + "Impl",对应的实现类是org.apache.dubbo.demo.DemoServiceImpl
        }
    } else if (ProviderConfig.class.equals(beanClass)) { //对<dubbo:provider> 中的嵌套<dubbo:service> 元素进行解析
        parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
    } else if (ConsumerConfig.class.equals(beanClass)) { //对<dubbo:consumer> 中的嵌套<dubbo:reference> 元素进行解析
        parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
    }
    Set<String> props = new HashSet<>();
    ManagedMap parameters = null; //托管的Map(Spring的标签集合类,用于保存被托管的Map)
    for (Method setter : beanClass.getMethods()) { //遍历Config中的set方法提取出属性名,然后通过XML的Element解析出对应的属性值,最后依次设置到Config类关联的Bean的属性中
        String name = setter.getName();
        if (name.length() > 3 && name.startsWith("set")
                && Modifier.isPublic(setter.getModifiers())
                && setter.getParameterTypes().length == 1) {
            Class<?> type = setter.getParameterTypes()[0];
            String beanProperty = name.substring(3, 4).toLowerCase() + name.substring(4); //解析出属性名,如方法名为setName,属性名为name
            String property = StringUtils.camelToSplitName(beanProperty, "-"); //按分隔符方式处理属性名称
            props.add(property); //将属性加到属性集合中
            // check the setter/getter whether match (检查set、get方法是否匹配)
            Method getter = null;
            try {
                getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);//获取指定的属性对应的get方法,如getName
            } catch (NoSuchMethodException e) {
                try {
                    getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]); //处理is开头的方法,比如ApplicationConfig中的isDefault()方法
                } catch (NoSuchMethodException e2) { //允许没有get方法,比如EnvironmentAware,只有set方法,不能抛出异常,不然引起应用启动失败
                    // ignore, there is no need any log here since some class implement the interface: EnvironmentAware,
                    // ApplicationAware, etc. They only have setter method, otherwise will cause the error log during application start up.
                }
            }
            if (getter == null
                    || !Modifier.isPublic(getter.getModifiers())
                    || !type.equals(getter.getReturnType())) { //若没有找到符合条件的get方法,则本次不处理,跳到下次循环
                continue;
            }

            /**
             * 从XML元素中读取Config类属性对应的值,并设置到Bean的属性中
             */
            if ("parameters".equals(property)) {
                parameters = parseParameters(element.getChildNodes(), beanDefinition, parserContext); //解析<dubbo:parameter/>元素的值
            } else if ("methods".equals(property)) {
                parseMethods(id, element.getChildNodes(), beanDefinition, parserContext); //解析<dubbo:method/>元素的值
            } else if ("arguments".equals(property)) {
                parseArguments(id, element.getChildNodes(), beanDefinition, parserContext); //解析<dubbo:argument/>元素的值
            } else { //对常规属性做处理
                String value = resolveAttribute(element, property, parserContext); //解析XML中元素对应的属性值
                if (value != null) { //若属性值为不为空,则对应处理(即XML中设置了对应的属性值)
                    value = value.trim(); //过滤字符串的前后空格(即过滤掉空字符串)
                    if (value.length() > 0) {
                        if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
                            RegistryConfig registryConfig = new RegistryConfig();
                            registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
                            beanDefinition.getPropertyValues().addPropertyValue(beanProperty, registryConfig);
                        } else if ("provider".equals(property) || "registry".equals(property) || ("protocol".equals(property) && AbstractServiceConfig.class.isAssignableFrom(beanClass))) {
                            // Class中方法isAssignableFrom():判断当前class是否与参数中指定的class相同,或者是参数指定的class的父类或父接口(主语是当前class,如上的主语即为:AbstractServiceConfig)
                            beanDefinition.getPropertyValues().addPropertyValue(beanProperty + "Ids", value);
                        } else {
                            Object reference;
                            if (isPrimitive(type)) { //对基本类型的属性处理(基本类型进行了扩展,包含String、Date等)
                                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(向后兼容旧版本的XSD中的默认值)
                                    value = null; //若旧版本的属性满足条件,则将属性值设置为null
                                }
                                reference = value;
                            // 对方法<dubbo:method>元素中的onreturn、onthrow、oninvoke属性进行处理
                            } else if (ONRETURN.equals(property) || ONTHROW.equals(property) || ONINVOKE.equals(property)) {
                                int index = value.lastIndexOf("."); //待覆盖调试:事件通知允许Consumer端在调用之前、调用之后或出现异常时,触发oninvoke、onreturn、onthrow三个事件。 https://dubbo.apache.org/zh/docs/advanced/events-notify/
                                String ref = value.substring(0, index); //onreturn、onthrow、oninvoke的属性值,必须要以 xxx.方法名形式,若没有".",则会由于[0,-1)报字符串区间错误 "String index out of range: -1"
                                String method = value.substring(index + 1);
                                reference = new RuntimeBeanReference(ref);
                                beanDefinition.getPropertyValues().addPropertyValue(property + METHOD, method);
                            } else { //解析ref属性<property name="ref">
                                if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
                                    BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                                    if (!refBean.isSingleton()) { //检查暴露的服务是不是单例
                                        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
                            }
                            beanDefinition.getPropertyValues().addPropertyValue(beanProperty, reference); //为bean添加属性值
                        }
                    }
                }
            }
        }
    }
    
    NamedNodeMap attributes = element.getAttributes(); //获取元素的属性节点列表
    int len = attributes.getLength();
    for (int i = 0; i < len; i++) {
        Node node = attributes.item(i);
        String name = node.getLocalName();
        if (!props.contains(name)) { //处理未处理过的属性节点
            if (parameters == null) {
                parameters = new ManagedMap();
            }
            String value = node.getNodeValue();
            parameters.put(name, new TypedStringValue(value, String.class));
        }
    }
    if (parameters != null) { //设置beanDefinition的parameters属性值
        beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
    }
    return beanDefinition;
}
  • 代码解析:
    • 代码中的RootBeanDefinition时Spring的Bean,可以作为其他BeanDefinition的父BeanDefinition,也可以单独作为BeanDefinition,但是不能作为其他BeanDefinition的子BeanDefinition。
    • 代码中的NamedNodeMap代表一个节点的无序列表,可以通过它们的名称进行访问。

解析内嵌元素

java 复制代码
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) {
        return;
    }
    boolean first = true;
    for (int i = 0; i < nodeList.getLength(); i++) { //可以有多个内嵌节点
        Node node = nodeList.item(i);
        if (!(node instanceof Element)) {
            continue;
        }
        if (tag.equals(node.getNodeName()) //带上命名空间的节点名称,如dubbo:service(按指定的tag查找Node)
                || tag.equals(node.getLocalName())) { //去掉命名空间的名称,如service
            if (first) { //有多个元素时,如<dubbo:service>,只有第一个元素才处理default值
                first = false; //处理好第一个元素后,更改该标志
                String isDefault = resolveAttribute(element, "default", parserContext); // default: 是否为缺省协议,用于多协议
                if (StringUtils.isEmpty(isDefault)) { //处理默认属性default
                    beanDefinition.getPropertyValues().addPropertyValue("default", "false");
                }
            }
            // 内部嵌套的元素,按单个元素解析的方式依次解析
            BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required); //获取嵌套元素对应的Bean,如<dubbo:provider>中<dubbo:service>
            if (subDefinition != null && StringUtils.isNotEmpty(ref)) { //依赖的bean用RuntimeBeanReference表示
                subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
            }
        }
    }
}
  • 代码解析:
    • BeanDefinition:在Spring中,Bean的解析阶段,会把xml配制中的标签解析成Spring中的BeanDefinition对象
    • RuntimeBeanReference:如果一个bean依赖其它的bean,比如dubbo:service中ref,那么被依赖的bean就用RuntimeBeanReference表示(因为解析阶段,还没有依赖的bean的实例,等解析以后存在实例时,再根据RuntimeBeanReference关联)

解析元素属性值

java 复制代码
private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) {
    if (nodeList == null) {
        return;
    }
    for (int i = 0; i < nodeList.getLength(); i++) { //NodeList: 提供了有序节点的抽象节点的集合
        if (!(nodeList.item(i) instanceof Element)) {
            continue;
        }
        Element element = (Element) nodeList.item(i);
        if ("property".equals(element.getNodeName()) //只对属性节点处理<property>
                || "property".equals(element.getLocalName())) {
            String name = resolveAttribute(element, "name", parserContext); //解析<property>中的name属性
            if (StringUtils.isNotEmpty(name)) {
                String value = resolveAttribute(element, "value", parserContext);
                String ref = resolveAttribute(element, "ref", parserContext);
                if (StringUtils.isNotEmpty(value)) { //解析基本属性
                    beanDefinition.getPropertyValues().addPropertyValue(name, value); //属性值列表beanDefinition.getPropertyValues(), 对应<property>的值
                } else if (StringUtils.isNotEmpty(ref)) { //解析含有引用的属性,创建对应的bean
                    beanDefinition.getPropertyValues().addPropertyValue(name, new RuntimeBeanReference(ref));
                } else {
                    throw new UnsupportedOperationException("Unsupported <property name=\"" + name + "\"> sub tag, Only supported <property name=\"" + name + "\" ref=\"...\" /> or <property name=\"" + name + "\" value=\"...\" />");
                }
            }
        }
    }
}

解析参数标签

java 复制代码
private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) {
    if (nodeList == null) { //NodeList表示一个有顺序的节点列表
        return null;
    }
    ManagedMap parameters = null; //托管的Map,用来保存map的值
    for (int i = 0; i < nodeList.getLength(); i++) { //可以有多个元素,如:<dubbo:parameter>
        if (!(nodeList.item(i) instanceof Element)) { //若元素不是Element实例,则不处理
            continue;
        }
        Element element = (Element) nodeList.item(i);
        if ("parameter".equals(element.getNodeName())
                || "parameter".equals(element.getLocalName())) { //只处理<dubbo:parameter/>
            if (parameters == null) {
                parameters = new ManagedMap();
            }
            String key = resolveAttribute(element, "key", parserContext); //解析<dubbo:parameter> 元素中的key
            String value = resolveAttribute(element, "value", parserContext); //解析<dubbo:parameter> 元素中的value
            boolean hide = "true".equals(resolveAttribute(element, "hide", parserContext));
            if (hide) { //是否隐藏,若需要隐藏,加上前缀
                key = HIDE_KEY_PREFIX + key;
            }
            parameters.put(key, new TypedStringValue(value, String.class)); //多个 <dubbo:parameter>时,若key重复,value值会被覆盖(TypedStringValue:存储了string值,已经要转换的目标类型,类型转换由bean工厂处理)
        }
    }
    return parameters; //此处没有直接添加到bean的属性中,而是在DubboBeanDefinitionParser#parse方法的最后添加的
}

DubboNamespaceHandler类分析

主要成员方法分析

初始化方法

java 复制代码
public void init() { //在解析自定义元素前,进行初始化操作(设置父类NamespaceHandlerSupport的Map<String, BeanDefinitionParser> parsers,即设置元素名与bean解析器的映射)
    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
    registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
    registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
    registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
    registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
    registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
    registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.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)); //将元素名与对应的bean进行对应
    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); //对应注解解析器
}
  • 代码解析:
    • init()方法解析:
      • 重写Spring NamespaceHandler的init()方法,
      • 在解析XML中的命名空间url时,如xmlns:dubbo="http://dubbo.apache.org/schema/dubbo",会调用init()方法,
      • 调用的地方org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve
    • registerBeanDefinitionParser()方法解析:
      • 重写Spring NamespaceHandlerSupport#registerBeanDefinitionParser()方法
      • 将元素名,如"application"与对应的解析器按键值对存储起来 Map<String, BeanDefinitionParser> parsers
      • 注册以后当前对象DubboNamespaceHandler从NamespaceHandlerSupport继承的私有成员变量parsers就有相关值了

问题点答疑

  • DubboBeanUtils#registerCommonBeans功能用途是什么?

    • 解答:用来注册含有基本功能的bean,如DubboBootstrapApplicationListener、ReferenceAnnotationBeanPostProcessor等等
      会打印出"The Infrastructure bean definition"日志
  • 是怎样加载到自定义的DubboNamespaceHandler的?

    • 解答:当Spring读取XML时,遇到非Spring定义XML元素时,就会查找spring.schemas和spring.handlers,在spring.handlers中配置了DubboNamespaceHandler处理器。

归纳总结

  • XML关联信息介绍

    • XML指可扩展标记语言、被设计用来传输和存储数据。HTML被设计用来显示数据
    • 第一行是XML声明,XML文档必须包含根元素(有且只有一个根元素)。该元素是所有其他元素的父元素。XML文档形成一种树结构。
    • XML元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。元素可包含其他元素、文本或者两者的混合物。元素也可以拥有属性。
    • 拥有正确语法的XML被称为"形式良好"的XML。可以通过DTD、Schema验证的XML是否是"合法"的XML。
    • 解析器把XML转换为XML DOM对象,XML DOM (XML Document Object Model) 定义了访问和操作XML文档的标准方法。
    • XML命名空间:提供避免元素命名冲突的方法。所有 XML 文档中的文本均会被解析器解析。只有 CDATA 区段(CDATA section)中的文本会被解析器忽略
  • DOM中的节点Node介绍:

    • XML 文档中的每个成分都是一个节点。
    • DOM 对应的规定:
      • 整个文档是一个文档节点
      • 每个 XML 标签是一个元素节点
      • 包含在 XML 元素中的文本是文本节点
      • 每一个 XML 属性是一个属性节点
      • 注释属于注释节点
相关推荐
刘Java3 天前
Dubbo 3.x源码(26)—Dubbo服务引用源码(9)应用级服务发现订阅refreshServiceDiscoveryInvoker
java·dubbo·dubbo源码
码农老起12 天前
从RocketMQ到Dubbo:自研中间件技术的崛起
中间件·dubbo·rocketmq
huahailing102413 天前
apache-dubbo
apache·dubbo
博洋科技13 天前
关于网站的权重和百度蜘蛛爬虫的关系
小程序·dubbo·网站建设·1024程序员节·保定h5网站建设·保定网站建设
B1nna13 天前
外卖开发(七)——校验收货地址是否超出配送范围
开发语言·dubbo·lua
唐梓航-求职中15 天前
rpc-dubbo-多版本
网络协议·rpc·dubbo
西岭千秋雪_18 天前
Dubbo应用篇
java·微服务·dubbo
写bug写bug20 天前
一文搞懂分布式服务发布和引用(Dubbo 案例解读)
java·后端·dubbo
运维&陈同学21 天前
【Dubbo03】消息队列与微服务之dubbo-admin 二进制与编译安装
linux·运维·服务器·后端·微服务·云原生·架构·dubbo
lizz227625 天前
百度地图JSAPI WebGL v1.0类参考
dubbo·webgl