内容简介:在Spring继承dubbo时,会使用dubbo自定义的标签来定义相关的属性,常见的标签有<dubbo:application/>,<dubbo:registry/>,<dubbo:service/>等。对于这些标签的解析,dubbo都是使用的统一的方式,而最终注册到Spring容器中的是一个个的以Config结尾的bean,比如:ApplicationConfig,RegistryConfig,ServiceBean等。本文主要讲解dubbo是如何按照统一的方式解析这些自定义标签的。关于Spring对自
在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.DubboNamespaceHandler http\://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()
方法:
@Override public 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是如何进行自定义标签的解析的。
以上所述就是小编给大家介绍的《Dubbo标签解析详解 原 荐》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 安卓 so 文件解析详解
- 【Spring】BeanFactory 解析 bean 详解
- 成本计算引擎动态规则解析技术详解
- Reface.NPI 方法名称解析规则详解
- 详解js的作用域、预解析机制
- MySQL Binlog 解析工具 Maxwell 详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从问题到程序-用Python学编程和计算
裘宗燕 / 机械工业出版社 / 2017-6-1
本书是以Python为编程语言、面向计算机科学教育中的程序设计基础课程与编程初学者的入门教材和自学读物。本书以Python为工具,详细讨论了与编程有关的各方面问题,介绍了从初级到高级的许多重要编程技术。本书特别强调编程中的分析和思考、问题的严格化和逐步分解、语言结构的正确选择、程序结构的良好组织,以及程序的正确和安全。书中通过大量实例及其开发过程,展示了好程序的特征和正确的编程工作方法。此外,书中......一起来看看 《从问题到程序-用Python学编程和计算》 这本书的介绍吧!