(个人理解,如果有误,望请指正,谢谢)
在使用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里。
拉到最下面看到
这是不是有点熟悉了,对这里就是和dubbo:application对应的元素定义的地方。挑一个最常用的进行查看:dubbo:service:
<xsd:element name="service" type="serviceType">`
`<xsd:annotation>`
`<xsd:documentation>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>`
`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>`
`xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="class" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="path" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="provider" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="generic" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>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>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="group" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="deprecated" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="delay" type="xsd:string" use="optional" default="0">`
`<xsd:annotation>`
`<xsd:documentation>`
``
`xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="export" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>`
``
`xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="weight" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>`
``
`xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="document" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>`
``
`xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="dynamic" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>`
`xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="token" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="accesslog" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="executes" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="protocol" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>xsd:documentation>`
`xsd:annotation>`
`xsd:attribute>`
`<xsd:attribute name="warmup" type="xsd:string" use="optional">`
`<xsd:annotation>`
`<xsd:documentation>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 + name + "\"> sub tag, Only supported + name + "\" ref=\"...\" /> or + 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(" 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: + value + "\" scope=\"singleton\" ...>");`
`}`
`}`
`reference = new RuntimeBeanReference(value);`
`}
至此我们已经完成了,dubbo标签的解析工作,生成了多个BeanDefinition,spring进行注入的时候,就自动会根据BeanDefinition生成相应的config类,放到spring的上下文中。
接下来,用几个实例,来分析下标签的解析:
这个标签对应的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自定义标签结束
我写这个只是辅助你理解,但是一定要实际去读源码