Dubbo的服务暴露与服务消费原理(1)-基于XML配置原理解析

xml配置

我们先来看看服务提供者和服务消费者是如何配置xml的

(1)服务提供者配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    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">
 
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="hello-world-provider"  />
 
    <!-- 使用multicast广播注册中心暴露服务地址 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />
    
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />
 
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" />
 
    <!-- 和本地bean一样实现服务 -->
    <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl" />
</beans>

(2)服务消费者配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    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">
 
    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
    <dubbo:application name="hello-world-consumer"  />
 
    <!-- 使用multicast广播注册中心暴露发现服务地址 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />
 
    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" />
</beans>

(3)服务提供者

public class XmlProvider {

    public static void main(String[] args) {
        System.setProperty("java.net.preferIPv4Stack", "true");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
        context.start();
        System.out.println("Provider started.");
    }

}

那dubbo是如何解析这些标签的呢?

源码分析

(1)进入BeanDefinitionParserDelegate,执行parseCustomElement方法,通过this.readerContext.getNamespaceHandlerResolver()​进入DefaultNamespaceHandlerResolver​。

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {  
   String namespaceUri = this.getNamespaceURI(ele);  
   //关键
  NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);  
 if (handler == null) {  
        this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);  
 return null;  } else {  
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));  
  }  
}

(2)进入DefaultNamespaceHandlerResolver,执行resolve方法,解析进入DubboNamespaceHandler。

 public NamespaceHandler resolve(String namespaceUri) {
        Map<String, Object> handlerMappings = this.getHandlerMappings();
        Object 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");
                } else {
                    NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
                    //关键
                    namespaceHandler.init();
                    handlerMappings.put(namespaceUri, namespaceHandler);
                    return namespaceHandler;
                }
            } catch (ClassNotFoundException var7) {
                throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);
            } catch (LinkageError var8) {
                throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8);
            }
        }
    }

this.handlerMappings是一个Map集合,为空的时候,就会去各个包下面的META-INF/spring.handlers中加载。

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
private Map<String, Object> getHandlerMappings() {
        if (this.handlerMappings == null) {
            synchronized(this) {
                if (this.handlerMappings == null) {
                    try {
                        Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                        }

                        Map<String, Object> handlerMappings = new ConcurrentHashMap(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                        this.handlerMappings = handlerMappings;
                    } catch (IOException var5) {
                        throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5);
                    }
                }
            }
        }

        return this.handlerMappings;
    }

(3)进入解析得到DubboNamespaceHandler,执行init方法,将标签关联到解析实现的类。

public void init() {
    this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
    this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
    this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
    this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
    this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
    this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
    this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    this.registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}

(4)DubboNamespaceHandler继承了NamespaceHandlerSupport,再来看看registerBeanDefinitionParser在NamespaceHandlerSupport中是如何定义的:

 protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
    this.parsers.put(elementName, parser);
}

(5)在BeanDefinitionParserDelegate.parseCustomElement()中通过handler.parse()方法进入NamespaceHandlerSupport。调用NamespaceHandlerSupport的findParserForElement返回DubboBeanDefinitionParser,然后调用DubboBeanDefinitionParser的parse方法。

public BeanDefinition parse(Element element, ParserContext parserContext) {
    return this.findParserForElement(element, parserContext).parse(element, parserContext);
}

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    String localName = parserContext.getDelegate().getLocalName(element);
    BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }

    return parser;
}

(6)进入DubboBeanDefinitionParser,执行parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
    return parse(element, parserContext, beanClass, required);
}

执行过程如下:

  • 实例化一个RootBeanDefinition,获取当前标签节点的属性id的值;如果id没有设置值,并且在(3)中设置的required的值为true,就去获取标签节点的属性name的值;如果name的值为空,判断实例Class是否是ProtocolConfig,如果是,就把generatedBeanName设置为dubbo,如果不是就获取属性interface的值;如果generatedBeanName继续为空,就获取实例Class的名称,将id设置为generatedBeanName的值,判断Spring上下文中是否存在相同的id,如果存在,就通过添加常数的方式获得新的id,然后保存在Spring上下文和RootBeanDefinition中。如果Spring上下文中存在相同的id,会抛出异常;
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
String id = element.getAttribute("id");
if ((id == null || id.length() == 0) && required) {
    String generatedBeanName = element.getAttribute("name");
    if (generatedBeanName == null || generatedBeanName.length() == 0) {
        if (ProtocolConfig.class.equals(beanClass)) {
            generatedBeanName = "dubbo";
        } else {
            generatedBeanName = element.getAttribute("interface");
        }
    }
    if (generatedBeanName == null || generatedBeanName.length() == 0) {
        generatedBeanName = beanClass.getName();
    }
    id = generatedBeanName;
    int counter = 2;
    while (parserContext.getRegistry().containsBeanDefinition(id)) {
        id = generatedBeanName + (counter++);
    }
}
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);
}
  • 依次看看<dubbo:protocol>、<dubbo:service>、<dubbo:provider>和<dubbo:consumer>是如何解析标签的。在Spring上下文中获取BeanDefinition,并遍历,如果获取的属性protocol的值是ProtocolConfig类,并且获取的name值与上面的id一致,就保存到BeanDefinition中;如果<dubbo:service>配置了class属性,那么为具体class配置的类注册Bean,并注入ref属性;
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));
            }
        }
    }
} 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);
        parseProperties(element.getChildNodes(), classDefinition);
        beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
    }
} else if (ProviderConfig.class.equals(beanClass)) {
    parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
} else if (ConsumerConfig.class.equals(beanClass)) {
    parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
}
  • 获取<dubbo:service>标签的<property>子标签,支持<property name="" ref="" /> or <property name="" value=""/>两种形式,并注入到BeanDifinition中
private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition) {
    if (nodeList != null && nodeList.getLength() > 0) {
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node instanceof Element) {
                if ("property".equals(node.getNodeName())
                        || "property".equals(node.getLocalName())) {
                    String name = ((Element) node).getAttribute("name");
                    if (name != null && name.length() > 0) {
                        String value = ((Element) node).getAttribute("value");
                        String ref = ((Element) node).getAttribute("ref");
                        if (value != null && value.length() > 0) {
                            beanDefinition.getPropertyValues().addPropertyValue(name, value);
                        } else if (ref != null && ref.length() > 0) {
                            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=\"...\" />");
                        }
                    }
                }
            }
        }
    }
}
  • <dubbo:provider>标签可以嵌套<dubbo:service>,<dubbo:consumer>标签可以嵌套<dubbo:reference>,解析内部的service生成bean的时候,会把外层的provider实例对象注入service,这种设计方式允许内部标签直接获取外部标签属性。
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) {
        boolean first = true;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node instanceof Element) {
                if (tag.equals(node.getNodeName())
                        || tag.equals(node.getLocalName())) {
                    if (first) {
                        first = false;
                        String isDefault = element.getAttribute("default");
                        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));
                    }
                }
            }
        }
    }
}
  • 循环遍历(3)中得到的每个解析标签实现类的方法,判断是不是公共的以set为前缀的方法且只有一个入参,如果是,获取对应的入参类型,方法名称和属性名,如public void setName(String name),则入参类型是java.lang.String,方法名称是setName,去掉set,然后首字母小写获得的就是属性名name。
  • 再通过is+方法名称(去掉set)或者get+方法名称(去掉set)获得一个getter方法,如果这个getter方法不为空,且是公共的,并且getter方法的返回类型等于setter方法的入参类型,就继续下一步判断,否则continue跳出当前循环。
Set<String> props = new HashSet<String>();
ManagedMap parameters = null;
for (Method setter : beanClass.getMethods()) {
    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 property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
        props.add(property);
        Method getter = null;
        try {
            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())) {
            continue;
        }
        //省略
    }
}
  • 如果属性名是parameters,就进行解析,将获取的每个parameter节点的key和value保存到一个Map集合中,注意:如果parameter的属性hide的值为true,需要在key前面加一个隐藏前缀才能保存在集合中。当属性名是methods,arguments时同理;
private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) {
    if (nodeList != null && nodeList.getLength() > 0) {
        ManagedMap parameters = null;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node instanceof Element) {
                if ("parameter".equals(node.getNodeName())
                        || "parameter".equals(node.getLocalName())) {
                    if (parameters == null) {
                        parameters = new ManagedMap();
                    }
                    String key = ((Element) node).getAttribute("key");
                    String value = ((Element) node).getAttribute("value");
                    boolean hide = "true".equals(((Element) node).getAttribute("hide"));
                    if (hide) {
                        key = Constants.HIDE_KEY_PREFIX + key;
                    }
                    parameters.put(key, new TypedStringValue(value, String.class));
                }
            }
        }
        return parameters;
    }
    return null;
}
  • 获取配置文件xml标签节点属性的值value,如:获取<dubbo:application name="hello-world-provider" />这个标签的属性name的值,如果值不为空,去掉空格长度大于0,把(属性名,值)保存在RootBeanDefinition类中。

参考

https://blog.csdn.net/qq_2763...

《深入理解Apache Dubbo与实战》

猜你喜欢

转载自blog.csdn.net/u012734723/article/details/107467997