一步步解析Dubbo之自定义标签

(个人理解,如果有误,望请指正,谢谢)

在使用dubbo的时候我们都是使用了dubbo的自定义的注解,接下来来分析下dubbo的自定义标签:

首先我们列举下dubbo的标签有哪些:

dubbo:application,dubbo:registry,dubbo:protocol,dubbo:provider,dubbo:consumer,dubbo:service,dubbo:reference

这些是我们常用的标签,那是不是只有这些呢?凡事都是有源头,到dubbo自定义标签的源头看看就知道了(dubbo.xsd);

一个xml的定义需要一个xsd:

我们在dubbo的配置文件里都会看到这一句:xmlns:dubbo=“http://code.alibabatech.com/schema/dubbo”,再看下面一句:http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd 这便是开始的地方,那我们就前往看看这个dubbo.xsd文件吧。

这个文件在dubbo-config-spring里。

img

拉到最下面看到

在这里插入图片描述

这是不是有点熟悉了,对这里就是和dubbo:application对应的元素定义的地方。挑一个最常用的进行查看:dubbo:service:

<xsd:element name="service" type="serviceType">`

​    `<xsd:annotation>`

​        `<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>`

​    `</xsd:annotation>`

`</xsd:element`>

发现里面什么都没有,但是type=serviceType,再看下serviceType:

<xsd:complexType name="serviceType">`

​    `<xsd:complexContent>`

​        `<xsd:extension base="abstractServiceType">`

​            `<xsd:choice minOccurs="0" maxOccurs="unbounded">`

​                `<xsd:element ref="method" minOccurs="0" maxOccurs="unbounded"/>`

​                `<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>`

​                `<xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded"/>`

​            `</xsd:choice>`

​            `<xsd:attribute name="interface" type="xsd:token" use="required">`

​                `<xsd:annotation>`

​                    `<xsd:documentation>`

​                        `<![CDATA[ Defines the interface to advertise for this service in the service registry. ]]></xsd:documentation>`

​                    `<xsd:appinfo>`

​                        `<tool:annotation>`

​                            `<tool:expected-type type="java.lang.Class"/>`

​                        `</tool:annotation>`

​                    `</xsd:appinfo>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="ref" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation>`

​                        `<![CDATA[ The service implementation instance bean id. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="class" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service implementation class name. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="path" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service path. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="provider" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ Deprecated. Replace to protocol. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="generic" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ Generic service. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:anyAttribute namespace="##other" processContents="lax"/>`

​        `</xsd:extension>`

​    `</xsd:complexContent>`

`</xsd:complexType>

complexType 元素定义复杂类型。复杂类型的元素是包含其他元素和/或属性的 XML 元素,我们看到xsd:extension发现继承自abstractServiceType。

我们再看下abstractServiceType标签:

<xsd:complexType name="abstractServiceType">`

​    `<xsd:complexContent>`

​        `<xsd:extension base="abstractInterfaceType">`

​            `<xsd:attribute name="register" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service can be register to registry. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service version. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="group" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service group. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="deprecated" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ whether the service is deprecated. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="delay" type="xsd:string" use="optional" default="0">`

​                `<xsd:annotation>`

​                    `<xsd:documentation>`

​                        `<![CDATA[ The service export delay millisecond. ]]>`

​                    `</xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="export" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation>`

​                        `<![CDATA[ The service is export. ]]>`

​                    `</xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="weight" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation>`

​                        `<![CDATA[ The service weight. ]]>`

​                    `</xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="document" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation>`

​                        `<![CDATA[ The service document. ]]>`

​                    `</xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="dynamic" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation>`

​                        `<![CDATA[ the service registered to the registry is dynamic(true) or static(false). ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="token" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service use token. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="accesslog" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service use accesslog. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="executes" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service allow execute requests. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="protocol" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:attribute name="warmup" type="xsd:string" use="optional">`

​                `<xsd:annotation>`

​                    `<xsd:documentation><![CDATA[ The warmup time in Milliseconds. ]]></xsd:documentation>`

​                `</xsd:annotation>`

​            `</xsd:attribute>`

​            `<xsd:anyAttribute namespace="##other" processContents="lax"/>`

​        `</xsd:extension>`

​    `</xsd:complexContent>`

`</xsd:complexType`>

结果发现又继承自abstractInterfaceType,由此我们一层层往下,将所有的属性都读一遍就了解了这个标签定义了什么。到这里,简单讲解下xml的定义。接下来,我们来看下spring的自定义标签

首先spring自定义标签需要两个额外的文件:

在这里插入图片描述

先来看下spring.schemas

http://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd

这里其实是定义命名空间的,这样当你在xml文件里指定了http://code.alibabatech.com/schema/dubbo/dubbo.xsd会找到dubbo.xsd,进行文件的解析。

再来看下spring.handlers

http://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

这个在前面的xml里有http://code.alibabatech.com/schema/dubbo,所以当spring发现是这个dubbo开始的自定义标签的时候,就用调用DubboNamespaceHandler来解析这个标签,那,接下来,我们去看下这个类:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {`



​    `static {`

​        `Version.checkDuplicate(DubboNamespaceHandler.class);`

​    `}`



​    `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());`

​    `}`



`}

这里几个常用的标签都在里面了,我们发现每个标签都是通过registerBeanDefinitionParser()方法来注册到spring中(这个是spring的知识点,就不进行太展开了)

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {

this.parsers.put(elementName, parser);

}

看到registerBeanDefinitionParser把元素名和解析类放到map里,再看下会发现

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;`

`}

所以,我们看到,如果xml解析读到对应的标签的时候,会从parsers这个map里取出解析类,调用解析类的parse方法,进行解析,(spring的解释就到这里吧,不再深入了)所以可以看到实际的解析是放在了DubboBeanDefinitionParser里进行的,看到这里,我们看到每个标签名对应一个配置类,如:application对应的是ApplicationConfig类。那接下来,看下DubboBeanDefinitionParser的parse方法是如何进行解析的:

RootBeanDefinition beanDefinition = new RootBeanDefinition();

beanDefinition.setBeanClass(beanClass);

beanDefinition.setLazyInit(false);

首先创建一个beanDefinition这个是spring IOC注入的初始形态,就是bean的定义类,到时候,实例化会转成BeanWrapper类。将传入的Class设置成当前,并设置成惰性加载。

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);`

`}

首先第一步要处理的当然是id,所以先去获取元素中的id属性,如果没有定义id,id又是必须的,那就去获取name属性,如果name属性还是没有,如果当前的Class是ProtocolConfig的话,就用默认的dubbo,如果不是,就在获取interface属性。最后还是没有,那只能多去当前Class的全限名作为id;

检查当前上下文中有没有注册重名的,如果重名了,后面数字累加。最后id不为空的情况下,将当前的类定义,注册到spring的上下文中,这样就能被spring自动注入了。这里完成了属性id的解析

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);`

`}

接下来,判断要解析的类型

1.protocolConfig ,先获取上下文中所有已经注册类定义,判断类定义中有没有protocol属性,如果没有,则跳过,有,获取属性值value,如果value是ProtocolConfig类型,并且id等于value的name,的类定义,Protocol属性都指定到当前这个ProtocolConfig上。其实就是把已经完成包含指定了protocle名字一样的类定义中的值进行覆盖。

2.如果是ServiceBean类型,获取class属性,如果class属性不为空的情况下,生成一个新的类定义,并将className做为其class,然后把新的类定义放到ref属性中,定义的名字是当前id +Impl。这里就是指定实现类。其中有一个方法parseProperties(element.getChildNodes(),classDefinitio),这个方法从字面上看,是将子元素解析赋值给classDefinition.来看下这个方法:

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=\"...\" />");`

​                        `}`

​                    `}`

​                `}`

​            `}`

​        `}`

​    `}`

`}`

从源码可以看出,这里是解析子元素property的,然后将name和value设置到类定义中去。

3.为ProviderConfig,则会解析子元素

parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);

因为dubbo:provider下面可以包含dubbo: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));`

​                    `}`

​                `}`

​            `}`

​        `}`

​    `}`

`}

这里看到tag是service,从代码看,其实这里解析provider的service,如果provider下有service,会生成两个BeanDefinition,一个是provider,一个是Service,

4.ConsumerConfig和ProviderConfig一样,就不再讲解

到这里,将一些固定标签的解析处理完,来完成剩下的解析。

首先,我们来说下@Parameter,这个标签是用来确认get方法是不是参数而放到当前的类定义中,如果@Parameter(key = Constants.APPLICATION_KEY, required = true),key是参数的名字,如果没有,这默认用get后面的名字作为,required是否必须@Parameter(excluded = true)这种表示不做为参数,排除掉 。知道这些了,那接下解析:

Set<String> props = new HashSet<String>();`

`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;`

​        `}

遍历传入的Class类中的方法,如果方法是set开头,是public修饰而且参数只有一个的,进行处理。先获取方法的参数为type,然后获取set后面的字符串,首字母小写,包含"-"转换成驼峰命名,以此作为property,将property放入props中,获取当前属性的get方法。

接着是对一些特殊property的处理:

\1. if (“parameters”.equals(property)) {parameters = parseParameters(element.getChildNodes(), beanDefinition);}

我们来看下parseParameters:

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;

}

从这里可以看出,如果class当前属性是parameters的时候,会去解析当前元素下的dubbo:parameter子元素,然后在parseParameter方法中将parameter子元素里的key,value解析出来(如果属性hide为true,则key会加上一个".",类似隐藏文件)然后返回这个ManageMap给主方法中。主方法中一开始定义的parameters就会被赋予这个值。

\2. else if (“methods”.equals(property)) 当前解析出来的property为methods时,调用parseMethods方法,我们来看下这个方法:

private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,

​                                 ParserContext parserContext) {if (nodeList != null && nodeList.getLength() > 0) {

​        ManagedList methods = null;for (int i = 0; i < nodeList.getLength(); i++) {

​            Node node = nodeList.item(i);

​            `if (node instanceof Element) {`

​                `Element element = (Element) node;`

​                `if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) {`

​                    `String methodName = element.getAttribute("name");`

​                    `if (StringUtils.isEmpty(methodName)) {`

​                        `throw new IllegalStateException("<dubbo:method> name attribute == null");`

​                    `}`

​                    `if (methods == null) {`

​                        `methods = new ManagedList();`

​                    `}`

​                    `BeanDefinition methodBeanDefinition = parse(((Element) node),`

​                            `parserContext, MethodConfig.class, false);`

​                    `String name = id + "." + methodName;`

​                    `BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(`

​                            `methodBeanDefinition, name);`

​                    `methods.add(methodBeanDefinitionHolder);`

​                `}`

​            `}`

​        `}`

​        `if (methods != null) {`

​            `beanDefinition.getPropertyValues().addPropertyValue("methods", methods);`

​        `}`

​    `}`

`}

从代码中可以看出,这里会去解析当前元素下,标签为dubbo:method的子元素,并且会用一个ManageList来存放这些method。当解析出当前的method名字不为空的时候,说明标签正确,调用parse解析method标签。(这样一来,就有多个method的BeanDefinition会被注册到spring上下文中。)然后以id+methodName为key放到BeanDefinitionHolder中,添加到methods的ManageList中,再在主标签的BeanDefinition中添加methods属性,这样实例化的时候,就会包含这些methods了。

3.else if (“arguments”.equals(property))调用parseArguments方法。(和parseMethods类似,不贴出代码了)

当方法中的属性为argumets的时候,同样解析dubbo:argument,过程和methods差不多,就不做过多解析了。

4.就是剩下的情况了,当前property既不是parameters,methods,也不是arguments:

首先获取元素中的为property的属性值value,如果value为空,直接继续循环,不处理,如果不是这里又分成了三种情况来处理

4.1.如果property是registry并且value是N/A(表示注册地址无效)

RegistryConfig registryConfig = new RegistryConfig();`

`registryConfig.setAddress(RegistryConfig.***NO_AVAILABLE***);`

`beanDefinition.getPropertyValues().addPropertyValue(beanProperty, registryConfig);

则创建一个registryConfig,然后以没有做过驼峰处理的属性名做可以,设置到BeanDefintion中

4.2.如果property是provider,registry(这里的registry已经是有效的),或protocel且当前beanClass是ServiceBean.class的情况下

beanDefinition.getPropertyValues().addPropertyValue(beanProperty + "Ids", value);

其实就是设置registryIds,providerIds,以及protocolIds的值。

4.3 对registry,provider,protocol这三个属性执行parseMutilRef方法

} else if ("registry".equals(property) && value.indexOf(',') != -1) {`

​    `parseMultiRef("registries", value, beanDefinition, parserContext);`

`} else if ("provider".equals(property) && value.indexOf(',') != -1) {`

​    `parseMultiRef("providers", value, beanDefinition, parserContext);`

`} else if ("protocol".equals(property) && value.indexOf(',') != -1) {`

​    `parseMultiRef("protocols", value, beanDefinition, parserContext);

parseMultiRef方法就是讲val包装成list.add(new RuntimeBeanReference(v));,然后添加对应的属性,key为传入的值,value就是这个list。

4.4处理上面的情况的剩下的处理方式:

如果property是私有变量,属性名是:async,timeout,delay,version,stat,reliable这些,且value值是无效的,那直接将value=null,然后,添加到beanDefinition的属性中,属性名就是未驼峰处理过的beanProperty,值就是value;

如果property是onreturn,onthrow,oninvoke,先将property为key,通过value解析出的方法名为值设置到beanDefinition中,然后然后再添加属性名为beanProperty,值为new RuntimeBeanReference(解析出的类)到beanDefinition中。

最后处理剩余情况,如果剩下的情况中,property为ref,则通过注册上下文中查看该映射类有没有被注册,如果有的话,就判断下是不是单例,如果不是单例,则报错。最后,生成reference = new RuntimeBeanReference(value);,用beanProperty为key,refrence为value,添加到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);`

`}

至此我们已经完成了,dubbo标签的解析工作,生成了多个BeanDefinition,spring进行注入的时候,就自动会根据BeanDefinition生成相应的config类,放到spring的上下文中。

接下来,用几个实例,来分析下标签的解析:

<dubbo:application name="testApp" />

这个标签对应的Config类是ApplicationConfig,所以调用解析类,生成一个BeanDefiniton,它的beanClass是ApplicationConfig.class,由于id没有值,id=name=“testApp”,BeanDefinition的属性,id也为testApp.而没有其他的属性值,所以,生成的BeanDefinition为:

beanClass:Application.class`

`lazyInit:false`

`propertys:`

`id : testApp

接下类我们分析一个比较复杂的

<dubbo:provider protocol="dubbo" timeout="3000" >`

   `<dubbo:service interface="testServie" class="com.tom.MyTest">`

​      `<dubbo:method name="testMethod">`

​         `<dubbo:argument index="2"/>`

​      `</dubbo:method>`

​      `<dubbo:parameter key="x" value="y"/>`

`<dubbo:parameter key="1" value="2"/>`

   `</dubbo:service>`

`</dubbo:provider>

首先解析元素dubbo:provider,对应的是ProviderConfig,所以创建了一个RootBeanDefinition,beanClass被设置成ProviderConfig.class,lazyInit值为false。由于id,name以及interface都是为空,所以,id被设置成com.alibaba.dubbo.config.ProviderConfig,如果有多个id为ProviderConfig的话,要进行累加一个值,但是这里我们默认只有一个providerConfig被注册了。这个时候,将beanDefinition的propertys里key为id,值为:com.alibaba.dubbo.config.ProviderConfig.因为ProviderConfig所以,执行parseNested方法。

在parseNeted中,会去解析dubbo:service ,创建一个RootBeanDefinition,beanClass被设置成ServiceBean.class.由于id,name为空,interface不为空,所以,id为testService,当单独处理ServiceBean时,由于class为空,所以不执行,到达开始解析ServiceBean.class的属性,如果当前的class不为空,则需要再创建一个新的RootBeanDefinitioin,beanClass被设置成,com.tom.MyTest,再解析子元素中的property标签,将值设置进这个新的BeanDefinition的properties,这个新的BeanDefinition没有被注册到上下文中,再把这个beanDefinition设置到Service的BeanDefinition中。beanDefinition.getPropertyValues().addPropertyValue(“ref”, new BeanDefinitionHolder(classDefinition, id + “Impl”));

当解析到parameters属性是,调用得到一个ManageMap,里面有{“x”:“y”,“1”:“2”},继续解析,当解析到methods的时候,

这个时候会去解析dubbo:method,这个时候又会递归的调用parse类解析,又会创建一个RootBeanDefinition, beanClass被设置成MethodConfig.class,由于id为空,但是name有值,所以id=“testMethod”,beanDefinition会添加一个proptery,key为id,value为testMethod,然后继续解析,当解析到了argument,这个时候会调用parseArguments进行解析dubbo:argument

解析dubbo:argument,调用parse解析,创建了又一个RootBeanDefinition, beanClass 被设置成argumentConfig ,id为com.alibaba.dubbo.config.ArgumentConfig, 添加一个属性key为index,value为2.然后将生成的argumenConfig的BeanDefinition,放到Managelist中,返回个methodConfig的BeanDefinition,然后methodConfig的BeanDefinition的添加属性key为arguments,value为返回的ManageList,里面是刚才创建的argumentConfig. 再把生成的methodConfig的BeanDefinition,放到ManageList中,再将ManageList返回到Service的BeanDefinition中,service的BeanDefinition中添加属性key为methods ,value为包含methodConfig的BeanDefintion的ManageList.service的parse解析完成,再回到 parseNested方法中,这时候,如果ref不为空,且subBeanDefinition,由于传入的参数中ref值是id,是com.alibaba.dubbo.config.ProviderConfig,所以给subBeanDefinition添加属性,key为ref,value为new RuntimeBeanReference(ref),指向这个生产者。到这里Service的解析也完成了,继续执行provider属性的解析,获取到ProviderConfig中的属性进行解析,protocol属性,被解析成一个ManageList,里面有一个new RuntimerBeanReference(“dubbo”);然后,添加属性属性key为timeout,value为3000。到这里解析结束了

1.ProviderConfig的BeanDefinition` 

		beanClass : com.alibaba.dubbo.config.ProviderConfig.class

		lazyInit : false

		properties:

			id : com.alibaba.dubbo.config.ProviderConfigX

			protocols : [ RumtimerBeanRefernce("dubbo") ]

2.ServiceBean的BeanDefinition

	beanClass : com.alibaba.dubbo.config.ServiceBean.class

	lazyInit : false

	properties:

			id : testService

			x : y

			1 : 2

			methods : ManageList [ {testService.testMethod : methodBeanDefinition } ]

			provider :  com.alibaba.dubbo.config.ProviderConfigX

			ref :  {testServiceImpl : BeanDefinition{ beanClass : com.tom.MyTest}}

3.MethodConfig 的BeanDefintition(methodBeanDefinition)

		beanClass : com.alibaba.dubbo.config.MethodConfig.class

		lazyInit : false

		properties:

		id : testMethod

		arguments : ManagaList [{testMethod.2 : argumentBeanDefinition}]

4. ArgumentConfig的BeanDefinition(argumentBeanDefinition)

    beanClass : com.alibaba.dubbo.config.ArgumentConfig.class

   lazyInit : false

   properties:

 		id : com.alibaba.dubbo.config.ArgumentConfig

		index : 2

dubbo自定义标签结束

我写这个只是辅助你理解,但是一定要实际去读源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值