侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Dubbo标签解析详解

2023-12-11 星期一 / 0 评论 / 0 点赞 / 41 阅读 / 24590 字

在Spring继承dubbo时,会使用dubbo自定义的标签来定义相关的属性,常见的标签有<dubbo:application/>,<dubbo:registry/>,<dubbo:service/>

       在Spring继承dubbo时,会使用dubbo自定义的标签来定义相关的属性,常见的标签有<dubbo:application/>,<dubbo:registry/>,<dubbo:service/>等。对于这些标签的解析,dubbo都是使用的统一的方式,而最终注册到Spring容器中的是一个个的以Config结尾的bean,比如:ApplicationConfig,RegistryConfig,ServiceBean等。本文主要讲解dubbo是如何按照统一的方式解析这些自定义标签的。

       关于Spring对自定义标签的解析流程,读者可以阅读本人前面写的Spring自定义标签解析与实现。这里首先我们在spring定义bean的xml文件中,可以看到,dubbo对应的xmlns所指定的URL为

xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"

       这里我们全局搜索dubbo.apache.org/schema/dubbo,可以找到对应的spring.handlers文件如下:

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

       可以看到,dubbo自定义标签最终是由DubboNamespaceHandler进行处理的,通过前面对spring自定义标签的讲解我们知道,该类必然实现了NamespaceHandler接口,而我们只需要查看其init()方法是如何实现的即可。如下是该类的源码:

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());    }}

       可以看到,对于dubbo的各个子标签的bean的生成,最终都是通过DubboBeanDefinitionParser来实现的,而该Parser必然也实现了BeanDefinitionParser接口,最终通过其parse()方法来将标签属性解析为各个bean属性。这里我们直接阅读其parse()方法:

@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {    return parse(element, parserContext, beanClass, required);}@SuppressWarnings("unchecked")private static BeanDefinition parse(Element element, ParserContext parserContext,         Class<?> beanClass, boolean required) {    // 创建一个保存目标bean也就是对应的Config对象,如ApplicationConfig,RegistryConfig等的    // BeanDefinition对象,最终其会注册到BeanFactoryRegistry中    RootBeanDefinition beanDefinition = new RootBeanDefinition();    beanDefinition.setBeanClass(beanClass);    beanDefinition.setLazyInit(false);        // 这里会尝试获取当前标签的id值,如果当前标签不存在id值,则会根据以下策略来为其生成一个bean name:    // 1. 获取其name属性,将其作为当前bean的名称;    // 2. 如果name属性不存在,则获取其interface属性,将其作为bean的名称,这里如果beanClass    //    是ProtocolConfig,则直接以dubbo作为其名称,这是因为ProtocolConfig中没有interface属性;    // 3. 如果还是无法获取到名称,则直接以beanClass的名称作为其名称;    // 4. 到这里,也就能保证一定会获取到一个名称,但是很有可能该名称在当前spring容器中已经使用过了,    //    那么这里会判断当前容器中是否包含该名称,如果包含,则在一个无限循环中在其名称后加一个数字,    //    最终一定能够保证生成的名称是唯一的    String id = element.getAttribute("id");    if ((id == null || id.length() == 0) && required) {        // 获取name属性的值        String generatedBeanName = element.getAttribute("name");        if (generatedBeanName == null || generatedBeanName.length() == 0) {            if (ProtocolConfig.class.equals(beanClass)) {                generatedBeanName = "dubbo";            } else {                generatedBeanName = element.getAttribute("interface");            }        }        // 获取beanClass的名称        if (generatedBeanName == null || generatedBeanName.length() == 0) {            generatedBeanName = beanClass.getName();        }        id = generatedBeanName;                // 通过无限循环生成唯一的bean名称        int counter = 2;        while (parserContext.getRegistry().containsBeanDefinition(id)) {            id = generatedBeanName + (counter++);        }    }        // 将当前的BeanDefinition注册到BeanDefinitionRegistry中,并且这里会设置其id属性值    if (id != null && id.length() > 0) {        if (parserContext.getRegistry().containsBeanDefinition(id)) {            throw new IllegalStateException("Duplicate spring bean id " + id);        }        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);        beanDefinition.getPropertyValues().addPropertyValue("id", id);    }        // 这里判断当前注册的beanClass是否为ProtocolConfig,如果是,则在当前BeanDefinitionRegistry    // 中找到所有的包含这样一种属性的BeanDefinition,该属性名为protocol,属性值为ProtocolConfig    // 类型,如果找到了,则将当前生成的ProtocolConfig的属性注入到这些找到的BeanDefinition中    if (ProtocolConfig.class.equals(beanClass)) {        for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {            BeanDefinition definition = parserContext.getRegistry()                .getBeanDefinition(name);            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));                }            }        }    // 如果当前beanClass是ServiceBean,这种bean对应的标签是<dubbo:service/>,这里会获取该标签    // 中的class属性值,并以该class为准创建一个BeanDefinition,然后将该BeanDefinition作为当前    // BeanDefinition的ref属性注入其中。    // 这里parseProperties()方法会获取当前标签的所有<property/>子标签,    // 然后将其属性注入到新生成的BeanDefinition中    } else if (ServiceBean.class.equals(beanClass)) {        String className = element.getAttribute("class");        if (className != null && className.length() > 0) {            RootBeanDefinition classDefinition = new RootBeanDefinition();            classDefinition.setBeanClass(ReflectUtils.forName(className));            classDefinition.setLazyInit(false);            // 转换<property/>子标签的属性值            parseProperties(element.getChildNodes(), classDefinition);            beanDefinition.getPropertyValues().addPropertyValue("ref",                 new BeanDefinitionHolder(classDefinition, id + "Impl"));        }    // 这里判断beanClass是否为ProviderConfig类型,如果是该类型,则将相关逻辑委托给parseNested()    // 方法进行处理,该方法的主要有两个作用:    // 1. 获取第一个标签名为service的子标签,判断其是否有default属性,如果有,则将该属性设置为当前    //    BeanDefinition的default属性值,也就是将当前的provider作为默认的provider;    // 2. 遍历得到所有的标签名为service的子标签,通过递归的方式在当前BeanDefinitionRegistry中注册    //    注册ServiceBean,并且将其provider设置为当前父标签的provider。也就是说,通过这种方式,    //    我们可以为特定的ServiceBean自定义设置其provider配置。    } else if (ProviderConfig.class.equals(beanClass)) {        parseNested(element, parserContext, ServiceBean.class, true,             "service", "provider", id, beanDefinition);    // 这里的逻辑与上面的provider的处理方式一致,即配置一个默认的consumer,然后将其子标签中定义的    // reference设置默认的consumer为当前的consumer    } else if (ConsumerConfig.class.equals(beanClass)) {        parseNested(element, parserContext, ReferenceBean.class, false,             "reference", "consumer", id, beanDefinition);    }        // 除去上面的特殊情况以外,下面的逻辑主要目的是获取当前beanClass中的各个属性名,然后获取当前标签    // 中对应于该属性名的各个标签值,并将其转换到对应的属性中    Set<String> props = new HashSet<String>();    ManagedMap parameters = null;    for (Method setter : beanClass.getMethods()) {        // 获取当前beanClass中所有的set方法,并且通过该方法获取其后属性的名称        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 propertyName = name.substring(3, 4).toLowerCase() + name.substring(4);            String property = StringUtils.camelToSplitName(propertyName, "-");            props.add(property);            Method getter = null;            try {                // 判断当前set方法对应的属性是否有对应的get方法或is方法,如果没有则忽略该属性                getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);            } catch (NoSuchMethodException e) {                try {                    getter = beanClass.getMethod("is" + name.substring(3),                         new Class<?>[0]);                } catch (NoSuchMethodException e2) {}            }            if (getter == null                || !Modifier.isPublic(getter.getModifiers())                || !type.equals(getter.getReturnType())) {                // 没有get方法或is方法则忽略该属性                continue;            }                        if ("parameters".equals(property)) {                // 获取当前标签的所有名称为parameter的子标签,将该标签中设置的属性值注入到当前	            // BeanDefinition的parameters属性中                parameters = parseParameters(element.getChildNodes(), beanDefinition);            } else if ("methods".equals(property)) {                // 获取当前标签的所有名称为method的子标签,并将这每一个子标签都注册                // 为一个MethodConfig的对象,最终将这些对象注入到当前BeanDefinition                // 的methods属性中                parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);            } else if ("arguments".equals(property)) {                // 获取当前标签的所有名称为argument的子标签,并将这每一个子标签都注册为一个                // ArgumentConfig的对象,最终将这些对象注入到当前BeanDefinition                // 的arguments属性中                parseArguments(id, element.getChildNodes(),                     beanDefinition, parserContext);            } else {                // 如果当前属性名不是上述的几种特例情况,则会在当前标签中获取与属性名同名的标签的值,                // 如果该值为空,则不进行处理                String value = element.getAttribute(property);                if (value != null) {                    value = value.trim();                    if (value.length() > 0) {                        if ("registry".equals(property)                             && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {                            // 如果当前属性名为registry,并且其值为N/A,则为期生成一个空的                            // RegistryConfig对象注入到当前BeanDefinition中                            RegistryConfig registryConfig = new RegistryConfig();                            registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);                            beanDefinition.getPropertyValues()                                .addPropertyValue(property, registryConfig);                        } else if ("registry".equals(property)                                 && value.indexOf(',') != -1) {                            // 如果当前属性名为registry,并且其是包含多个注册中心的,                            // 则为这每一个注册中心都生成一个RegistryConfig对象,                            // 最终以list的形式保存到当前的BeanDefinition的registries属性中                            parseMultiRef("registries", value,                                 beanDefinition, parserContext);                        } else if ("provider".equals(property)                                 && value.indexOf(',') != -1) {                            // 如果当前属性名为provider,并且其是包含有多个提供者的,                            // 则为这每一个提供者都生成一个ProviderConfig对象,                            // 最终以list的形式保存到当前BeanDefinition的providers属性中                            parseMultiRef("providers", value,                                 beanDefinition, parserContext);                        } else if ("protocol".equals(property)                                 && value.indexOf(',') != -1) {                            // 如果当前属性名为protocol,并且其是包含有多个提供者的,                            // 则为这每一个protocol都生成一个ProtocolConfig对象,                            // 最终以list的形式保存到当前BeanDefinition的protocols属性中                            parseMultiRef("protocols", value,                                 beanDefinition, parserContext);                        } else {                            Object reference;                            if (isPrimitive(type)) {                                // 如果当前属性类型是基本数据类型,并且其值为默认值,                                // 则将当前属性设置为空                                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)) {                                    value = null;                                }                                reference = value;                            } else if ("protocol".equals(property)                                       && ExtensionLoader.getExtensionLoader(                                           Protocol.class).hasExtension(value)                                       && (!parserContext.getRegistry()                                           .containsBeanDefinition(value)                                           || !ProtocolConfig.class.getName()                                           .equals(parserContext.getRegistry()                                                   .getBeanDefinition(value)                                                   .getBeanClassName()))) {                                // 如果当前属性名为protocol,并且当前SPI中包含有该protocol,                                // 则为其生成一个ProtocolConfig对象,存入到BeanDefinition中                                if ("dubbo:provider".equals(element.getTagName())) {                                    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)) {                                // 如果当前的属性为onreturn,则获取当前属性值所指定的bean名称                                // 和方法名,将其设置到当前的BeanDefinition中                                int index = value.lastIndexOf(".");                                String returnRef = value.substring(0, index);                                String returnMethod = value.substring(index + 1);                                reference = new RuntimeBeanReference(returnRef);                                beanDefinition.getPropertyValues()                                    .addPropertyValue("onreturnMethod", returnMethod);                            } else if ("onthrow".equals(property)) {                                // 如果当前属性为onthrow,则获取该属性所指定的bean名称和方法名,                                // 将其设置到当前的BeanDefinition中                                int index = value.lastIndexOf(".");                                String throwRef = value.substring(0, index);                                String throwMethod = value.substring(index + 1);                                reference = new RuntimeBeanReference(throwRef);                                beanDefinition.getPropertyValues()                                    .addPropertyValue("onthrowMethod", throwMethod);                            } else if ("oninvoke".equals(property)) {                                // 如果当前属性为oninvoke,则获取该属性所指定的bean名称和方法名,                                // 将其设置到当前的BeanDefinition中                                int index = value.lastIndexOf(".");                                String invokeRef = value.substring(0, index);                                String invokeRefMethod = value.substring(index + 1);                                reference = new RuntimeBeanReference(invokeRef);                                beanDefinition.getPropertyValues()                                    .addPropertyValue("oninvokeMethod", invokeRefMethod);                            } else {                                // 如果属性名为ref,并且当前BeanDefinitionRegistry中包含有                                // 该名称的bean,则将该bean注入到当前BeanDefinition中                                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);                            }                            beanDefinition.getPropertyValues()                                .addPropertyValue(propertyName, reference);                        }                    }                }            }        }    }        // 对于那些在标签中存在,但是在当前beanClass中不存在的属性,dubbo会将其以键值对的形式    // 存入到当前BeanDefinition的parameters属性中    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.getPropertyValues().addPropertyValue("parameters", parameters);    }    return beanDefinition;}

       在上述解析过程中,dubbo首先会为当前BeanDefinition生成一个名称,然后判断当前beanClass是否为ProtocolConfig,ServiceBean,ProviderConfig和ConsumerConfig中的一个,如果是,则会进行一定的特殊解析。在特殊解析完成后,dubbo会获取当前beanClass的所有属性,然后在当前标签中查找对应的标签值,并将其设置到对应的属性中,最终完成所有属性值的装配。

       本文首先讲解了dubbo自定义标签的解析入口的查找方式,然后着重从源码的角度讲解了dubbo是如何进行自定义标签的解析的。

       读者朋友如果觉得本文还不错,可以点击下面的广告链接,这可以为作者带来一定的收入,从而激励作者创作更好的文章,非常感谢!

在项目开发过程中,企业会有很多的任务、需求、缺陷等需要进行管理,CORNERSTONE 提供敏捷、任务、需求、缺陷、测试管理、WIKI、共享文件和日历等功能模块,帮助企业完成团队协作和敏捷开发中的项目管理需求;更有甘特图、看板、思维导图、燃尽图等多维度视图,帮助企业全面把控项目情况。

广告 广告

评论区