欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

dubbo源码系列3——dubbo自定义标签解析

时间:2023-05-01

        在梳理完dubbo spi 机制后【dubbo源码系列1——spi源码解读(上)dubbo源码系列2——spi源码解读(下)】
本节开始梳理dubbo provider的启动流程,因基于dubbo源码进行研究,因此直接采用dubbo源码中dubbo-demo模块中xml配置示例作为demo。
        本节将解析dubbo是如何利用spring进行自定义标签解析并注册到容器中,本节安排如下:

demo解析流程分析总结 一、demo

接口

public interface DemoService { String sayHello(String name); default CompletableFuture sayHelloAsync(String name) { return CompletableFuture.completedFuture(sayHello(name)); }}

接口实现

public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @Override public String sayHello(String name) { logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } @Override public CompletableFuture sayHelloAsync(String name) { CompletableFuture cf = CompletableFuture.supplyAsync(() -> { return "async result"; }); return cf; }}

启动

public class ApplicationP { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml"); context.start(); }}

配置文件

二、解析流程分析 1、重要组件

dubbo.xsd
位于meta-INF下面,用于校验规范dubbo标签的编写;spring.schemas

http://dubbo.apache.org/schema/dubbo/dubbo.xsd=meta-INF/dubbo.xsdhttp://code.alibabatech.com/schema/dubbo/dubbo.xsd=meta-INF/compat/dubbo.xsd

xsd规范链接与实际xsd文件存放位置映射关系

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

标签命名空间与实际解析handler映射关系

2、流程分析

->AbstractApplicationContext#refresh ->AbstractApplicationContext#obtainFreshBeanFactory->AbstractRefreshableApplicationContext#refreshBeanFactory->AbstractXmlApplicationContext#loadBeanDefinitions->AbstractXmlApplicationContext#loadBeanDefinitions->AbstractBeanDefinitionReader#loadBeanDefinitions->XmlBeanDefinitionReader#loadBeanDefinitions->XmlBeanDefinitionReader#doLoadBeanDefinitions->XmlBeanDefinitionReader#registerBeanDefinitions->DefaultBeanDefinitiondocumentReader#registerBeanDefinitions->DefaultBeanDefinitiondocumentReader#parseBeanDefinitions

spring加载xml并解析成beanDefinition流程比较长,本节重点关注spring如何解析自定义标签解生成beanDefintion并注册到容器。

DefaultBeanDefinitiondocumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //判断是否为默认命名空间if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;//判断节点的命名空间是否为默认的beans,如果不是则走自定义解析if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}else {delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}

比如dubbo:application节点如下:

BeanDefinitionParserDelegate#parseCustomElement

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { //获取节点的命名空间String namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;} //this.readerContext.getNamespaceHandlerResolver()为DefaultNamespaceHandlerResolverNamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}

DefaultNamespaceHandlerResolver#resolve

public NamespaceHandler resolve(String namespaceUri) { //加载classpath下面所有的meta-INF/spring.handlers文件得到命名空间与handler的映射,如下截图所示Map handlerMappings = getHandlerMappings();//获取handlerObject handlerOrClassName = handlerMappings.get(namespaceUri);if (handlerOrClassName == null) {return null;}else if (handlerOrClassName instanceof NamespaceHandler) {return (NamespaceHandler) handlerOrClassName;}else {String className = (String) handlerOrClassName;try { //加载类Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");} //实例化handler,dubbo对应为DubboNamespaceHandlerNamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);//执行init方法namespaceHandler.init();handlerMappings.put(namespaceUri, namespaceHandler);return namespaceHandler;}catch (ClassNotFoundException ex) {throw new FatalBeanException("Could not find NamespaceHandler class [" + className +"] for namespace [" + namespaceUri + "]", ex);}catch (linkageError err) {throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +className + "] for namespace [" + namespaceUri + "]", err);}}}

DefaultNamespaceHandlerResolver#getHandlerMappings

private Map getHandlerMappings() {Map handlerMappings = this.handlerMappings;if (handlerMappings == null) {synchronized (this) {handlerMappings = this.handlerMappings;if (handlerMappings == null) {try { //加载meta-INF/spring.handlers文件Properties mappings =PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);handlerMappings = new ConcurrentHashMap<>(mappings.size()); //复制properties到map中CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);this.handlerMappings = handlerMappings;}catch (IOException ex) {throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);}}}}return handlerMappings;}


可以命名空间为http://dubbo.apache.org/schema/dubbo的标签应该用DubboNamespaceHandler解析;

DubboNamespaceHandler#init

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("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)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, true)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }

DubboNamespaceHandler#parse

public BeanDefinition parse(Element element, ParserContext parserContext) { //获取注册接口 BeanDefinitionRegistry registry = parserContext.getRegistry(); registerAnnotationConfigProcessors(registry); //注册beanProcessor,比如ReferenceAnnotationBeanPostProcessor registerCommonBeans(registry); //调用DubboBeanDefinitionParser的parse方法进行解析 BeanDefinition beanDefinition = super.parse(element, parserContext); setSource(beanDefinition); return beanDefinition; }

DubboBeanDefinitionParser#parse

private static RootBeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false); //优先使用id,次优使用name,最后使用interface.如果包含相同的beanName,则通过增加后缀id来区分 String id = resolveAttribute(element, "id", parserContext); if (StringUtils.isEmpty(id) && required) { String generatedBeanName = resolveAttribute(element, "name", parserContext); if (StringUtils.isEmpty(generatedBeanName)) { if (ProtocolConfig.class.equals(beanClass)) { generatedBeanName = "dubbo"; } else { generatedBeanName = resolveAttribute(element, "interface", parserContext); } } if (StringUtils.isEmpty(generatedBeanName)) { generatedBeanName = beanClass.getName(); } id = generatedBeanName; int counter = 2; while (parserContext.getRegistry().containsBeanDefinition(id)) { id = generatedBeanName + (counter++); } } if (StringUtils.isNotEmpty(id)) { if (parserContext.getRegistry().containsBeanDefinition(id)) { throw new IllegalStateException("Duplicate spring bean id " + id); } //手动注册 parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); beanDefinition.getPropertyValues().addPropertyValue("id", id); } //省略部分非关键代码 }

可以看到通过parserContext.getRegistry().registerBeanDefinition(id, beanDefinition)将自定义schema标签解析成beanDefinition注册到beanDefinitionMap中,解析完如下所示:

三、 总结

        通过自定义命名空间找到自定义NamespaceHandler,通过自定义handler解析自定义标签生成beanDefinition注册到容器中。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。