深圳做网站电话/网络优化包括
1:写在前面
在这篇文章 介绍了如何使用spring的自定义标签来生成spring bean,好处是灵活可定制,dubbo提供的xml配置也使用了这种方式来完成配置,下面我们一起来看下。
2:定义
先来看下相关的xsd,namespacehandler的配置。
2.1:xsd
根据spring的规范xsd需要定义在META-INF/spring.schemas
文件中,如下图:
内容如下:
http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd
其中的namespace是http\://code.alibabatech.com/schema/dubbo
,xsd是META-INF/compat/dubbo.xsd
。如下是示例xml命名空间和配置文件命名空间的对应关系:
如下是dubbo.xsd中定义的我们可以在xml配置文件中使用的配置项:
其中的element中的name就是我们在xml配置文件中使用的名称,如<xsd:element name="application" ...>
,对应的就是\<dubbo:application\>
。element中的type,就是元素可以定义的数据类型,如<xsd:element name="registry" type="registryType">
对应的就是复杂类型registryType
,配置了可以定义的元素,元素是否可选等信息,如下:
<xsd:complexType name="registryType"><!-- minOccurs="0"最小出现次数0,即可以不配置,也可以配置多次,配置多次可能有多个注册中心 --> <xsd:sequence minOccurs="0" maxOccurs="unbounded"><xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded"/></xsd:sequence><!-- spring bean的唯一标识 --> <xsd:attribute name="id" type="xsd:ID"><xsd:annotation><xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation></xsd:annotation></xsd:attribute><!-- 注册中心的地址 --> <xsd:attribute name="address" type="xsd:string" use="optional"><xsd:annotation><xsd:documentation><![CDATA[ The registry address. ]]></xsd:documentation></xsd:annotation></xsd:attribute><!-- 注册中心的端口 --> <xsd:attribute name="port" type="xsd:string" use="optional"><xsd:annotation><xsd:documentation><![CDATA[ The registry default port. ]]></xsd:documentation></xsd:annotation></xsd:attribute><!-- 注册中心的协议,如zookeeper,nacos --> <xsd:attribute name="protocol" type="xsd:string" use="optional"><xsd:annotation><xsd:documentation><![CDATA[ The registry lookup protocol. ]]></xsd:documentation></xsd:annotation></xsd:attribute><!-- ... -->
</xsd:complexType>
2.2:namespacehandler
对应的文件如下图:
内容如下:
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
可以看到对应的namespacehandler实现类是com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
,继承抽象类org.springframework.beans.factory.xml.NamespaceHandlerSupport
,源码如下:
// com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
public class DubboNamespaceHandler extends NamespaceHandlerSupport {static {Version.checkDuplicate(DubboNamespaceHandler.class);}@Overridepublic 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());}}
可以看到对各种标签提供了对应的DubboBeanDefinitionParser实例,负责将xml标签解析成bean definition,最终用于生成spring bean,这些类也正是本文要重点分析的内容,即如何解析xml配置文件为一个一个的bean定义。DubboBeanDefinitionParser实现了spring 定义的用于解析元素标签接口org.springframework.beans.factory.xml.BeanDefinitionParser
,因此接下来我们重点要看的类就是DubboBeanDefinitionParser了。注册完毕后如下图:
3:DubboBeanDefinitionParser
3.1:构造方法
class FakeCls {// xml配置对应的类配置类private final Class<?> beanClass;// 当没有设置id属性时,是否自动生成,对于不会被其他其他配置引用的配置可设置为false,如<dubbo:reference>private final boolean required;public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {this.beanClass = beanClass;this.required = required;}
}
3.2:解析方法
对应的方法是com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser.parse
,这是在spring的接口org.springframework.beans.factory.xml.BeanDefinitionParser
中定义的方法,源码如下:
// com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser.parse
class FakeCls {@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) { return parse(element, parserContext, beanClass, required);}
}
继续看:
class FakeCls {private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {// 创建bean定义,即返回的结果RootBeanDefinition beanDefinition = new RootBeanDefinition();// 设置要创建的spring bean类型beanDefinition.setBeanClass(beanClass);// 设置延迟加载,即只有在getBean获取,或者被注入到其他bean时才会创建,如果需要饥饿加载,可以配置<dubbo:reference ...init="true"> beanDefinition.setLazyInit(false);// 获取id属性,即spring bean名称String id = element.getAttribute("id");// 如果没有配置id属性,且要求自动生成if ((id == null || id.length() == 0) && required) {// 获取name属性,作为bean名称,这点和spring默认的规则是一致的,即优先使用id,然后再使用name String generatedBeanName = element.getAttribute("name");// 如果是没有配置name属性 if (generatedBeanName == null || generatedBeanName.length() == 0) {// 如果是<dubbo:protocol>,则使用dubbo作为bean名称,因为全局只有一个,所以可以直接硬编码,当然后续也有避免重复的逻辑处理// 如果不是<dubbo:protocol>,则使用interface的值,即服务接口全限定名作为bean名称if (ProtocolConfig.class.equals(beanClass)) {generatedBeanName = "dubbo";} else {generatedBeanName = element.getAttribute("interface");}}// 依然没有获取到bean名称,则使用class的名称作为结果,如java.lang.Object.class结果就是"java.lang.Object"if (generatedBeanName == null || generatedBeanName.length() == 0) {generatedBeanName = beanClass.getName();}// 赋值到id属性,作为bean名称id = generatedBeanName;// 如果是id已经存在则通过添加自增序列的方式解决重复// 如当前id是xxx,存在的bean定义id是xxx,xxx2,xxx3,则这里最终的结果就是xxx->xxx4int counter = 2;while (parserContext.getRegistry().containsBeanDefinition(id)) {id = generatedBeanName + (counter++);}}// 成功获取了spring bean IDif (id != null && id.length() > 0) {// 已经包含了该spring bean ID的bean定义,理论上这里不会为true,因为前面已经做了重复处理了// 可能是为了健壮性才写了这里的代码吧if (parserContext.getRegistry().containsBeanDefinition(id)) {throw new IllegalStateException("Duplicate spring bean id " + id);}// 注册bean定义// 到这里bean id和bean定义的对应关系已经注册成功了,但此时bean定义还只是个空壳,内部信息还需要依赖后续程序进行初始化parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);// 添加id属性到bean定义对象中beanDefinition.getPropertyValues().addPropertyValue("id", id);}// 2021-12-08 11:00:31if (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));}}}// 2021-12-08 14:20:21} 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"));}// 2021-12-08 15:40:51,解析<dubbo:provider>的<dubbo:service>子标签们} else if (ProviderConfig.class.equals(beanClass)) {parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);// 同2021-12-08 15:40:51,只不过这里是解析<dubbo:consumer>的<dubbo:reference>子标签们} else if (ConsumerConfig.class.equals(beanClass)) {parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);}Set<String> props = new HashSet<String>();ManagedMap parameters = null;// 获取所有的方法,通过其写方法,即setter来为属性赋值// 个人认为这里变量直接命名为不太合适,因为不一定是写方法,叫mayBeSetter可能更合适一些for (Method setter : beanClass.getMethods()) {String name = setter.getName();// Modifier.isPublic(setter.getModifiers()):是public的 // setter.getParameterTypes().length == 1:参数的个数是1个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);// 为"-"分割的字符串,如setMyFirstGirlFriend->my-first-girl-friendString property = StringUtils.camelToSplitName(propertyName, "-");props.add(property);Method getter = null;//*** 验证是否有getter开始 ***//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) {}}// !Modifier.isPublic(getter.getModifiers():getter不是public的// 或者!type.equals(getter.getReturnType()):和setter中的参数类型不一致,则忽略,继续下一个方法if (getter == null|| !Modifier.isPublic(getter.getModifiers())|| !type.equals(getter.getReturnType())) {continue;}//*** 验证是否有getter结束 ***//// 2021-12-08 17:44:39// 如果属性名称是parameters,即当前方法是setParameters,则循环处理<dubbo:parameters>子标签设置属性if ("parameters".equals(property)) {parameters = parseParameters(element.getChildNodes(), beanDefinition);//*** 属性为对象数据类型解析结束 ***// // 2021-12-08 18:04:14// 处理<dubbo:method>子标签} else if ("methods".equals(property)) {parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);// 2021-12-08 18:21:47} else if ("arguments".equals(property)) {parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);//*** 属性为对象数据类型解析结束 ***////*** 属性为String解析开始 ***//} else {// 获取普通的属性值String value = element.getAttribute(property);if (value != null) {value = value.trim();if (value.length() > 0) {// 配置service不注册到注册中心的情况,如配置:/*<dubbo:service interface="dongshi.daddy.service.RegistryNAService"ref="registryNAServiceImpl"registry="N/A"/>*/if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {// 因为设置了N/A,所以直接设置一个地址为N/A的RegistryConfigRegistryConfig registryConfig = new RegistryConfig();registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);// 配置使用多个注册中心的情况,如下配置:/*<dubbo:registry id="registry2181" address="zookeeper://192.168.10.119:2181" /><dubbo:registry id="registry2182" address="zookeeper://192.168.10.119:2182" /><dubbo:service interface="dongshi.daddy.service.RegistryNAService"ref="registryNAServiceImpl"registry="registry2181,registry2182"/>*/} else if ("registry".equals(property) && value.indexOf(',') != -1) {// 2021-12-09 17:32:02parseMultiRef("registries", value, beanDefinition, parserContext);// 配置多个provider,不知道什么时候有这种配置,先忽略!!!} else if ("provider".equals(property) && value.indexOf(',') != -1) {parseMultiRef("providers", value, beanDefinition, parserContext);// 多个协议配置,如配置:/*<dubbo:protocol id="protocol_1" name="dubbo" port="20826"/><dubbo:protocol id="protocol_2" name="dubbo" port="20827"/><dubbo:service interface="dongshi.daddy.service.RegistryNAService"ref="registryNAServiceImpl"registry="registry2181,registry2182" protocol="protocol_1,protocol_2"/>*/// 这样就会使用2个协议暴露两个地址,如/*[zk: localhost:2181(CONNECTED) 52] ls /dubbo/dongshi.daddy.service.RegistryNAService/providers [dubbo://192.168.64.1:20827/..., dubbo://192.168.64.1:20826/...]*/} else if ("protocol".equals(property) && value.indexOf(',') != -1) {// 参考2021-12-09 17:32:02parseMultiRef("protocols", value, beanDefinition, parserContext);} else {// 属性值Object reference;// 是基础数据类型,如name,host,port等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)) {// backward compatibility for the default value in old version's xsdvalue = null;}reference = value;// protocol可以在很多标签中使用,比如<dubbo:service>,<dubbo:provider>,而其值可能是一个<dubbo:protocol>的spring bean id// 也可能是一个协议的名称,此处就是处理这种缠绕的复杂情况的,即值有可能是一个bean ID,也有可能是协议对象,知道即可,先不深究} else if ("protocol".equals(property)&& ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)&& (!parserContext.getRegistry().containsBeanDefinition(value)|| !ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {if ("dubbo:provider".equals(element.getTagName())) {logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");}// backward compatibilityProtocolConfig protocol = new ProtocolConfig();protocol.setName(value);reference = protocol;// *** 事件通知相关属性开始 *** //// 2021-12-10 10:22:22} else if ("onreturn".equals(property)) {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)) {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)) {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引用其他spring bean的情况if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {// 获取引用的bean定义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);}// 添加值引用到当前标签的bean定义beanDefinition.getPropertyValues().addPropertyValue(propertyName, reference);}}}}//*** 属性为String解析结束 ***//}}// 解析类的属性的配置项,如<dubbo:service xxx="yyy">,在类中没有对应的setter,getter,则上述就解析不到,需要依靠这里的逻辑来解析// 可以认为不存在这种情况,即在标签中配置的属性,都是类属性,所以也可以忽略这里的逻辑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);}// 返回标签对应的bean定义return beanDefinition;}
}
2021-12-08 11:00:31
是处理\<dubbo:protocol\>
的情况,详细参考3.2.1:dubbo:protocol
。2021-12-08 14:20:21
处是处理\<dubbo:service\>
,详细参考3.2.2:dubbo:service
。2021-12-08 15:40:51
处是处理\<dubbo:provider\>
中存在\<dubbo:service\>
子标签的情况,如果是这种情况就不需要单独再定义dubbo:service了,如下可能配置:
<fakeRoot><dubbo:application name="dongshidaddy-provider" owner="dongshidaddy"/><dubbo:registry address="zookeeper://192.168.10.119:2181" /><dubbo:protocol id="dndndnddn" name="dubbo" port="20826"/><bean class="dongshi.daddy.service.MyPathServiceImpl" id="myPathService"/><dubbo:provider><dubbo:service interface="dongshi.daddy.service.MyPathService" class="dongshi.daddy.service.MyPathServiceImpl" id="myPathServiceId" path="toMyPath"><property name="name" value="张三11"/><property name="age" value="20"/></dubbo:service></dubbo:provider>
</fakeRoot>
详细参考3.2.3:dubbo:provider
。2021-12-08 17:44:39
处是解析paramters子标签,获取parameters属性值,如下就是这种配置:
<fakeRoot><dubbo:application name="dongshidaddy-provider" owner="dongshidaddy"><dubbo:parameter key="author" value="dongshidaddy"/><dubbo:parameter key="writeYear" value="2021"/></dubbo:application>
</fakeRoot>
源码如下:
class FakeCls {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) {// node.getLocalName():不带命名空间前缀的节点名称,这里如<dubbo:paramter>结果就是paramter// 而node.getNodeName()的结果是dubbo:parameterif ("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;}
}
2021-12-08 18:04:14
处是处理dubbo:method
子标签,如下可能配置:
<fakeRoot><dubbo:serviceinterface="dongshi.daddy.service.ProviderService"ref="providerService"><dubbo:method name="SayHello" timeout="2000"/></dubbo:service>
</fakeRoot>
源码如下:
class FakeClass {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 (methodName == null || methodName.length() == 0) {throw new IllegalStateException("<dubbo:method> name attribute == null");}if (methods == null) {methods = new ManagedList();}// 递归调用生成<dubbo:method>的MethodConig类对应的beandefinition对象BeanDefinition methodBeanDefinition = parse(((Element) node),parserContext, MethodConfig.class, false);// 设置方法对应的spring bean idString name = id + "." + methodName;BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(methodBeanDefinition, name);methods.add(methodBeanDefinitionHolder);}}}// 添加方法信息到当前解析的标签对应的bean定义中if (methods != null) {beanDefinition.getPropertyValues().addPropertyValue("methods", methods);}}}
}
2021-12-08 18:21:47
处是处理dubbo:argument
子标签,如配置:
<fakeRoot><dubbo:service interface="dongshi.daddy.service.MyCallbackService" ref="callbackService" connections="1" callbacks="1000"><dubbo:method name="addListener"><dubbo:argument type="dongshi.daddy.service.CallbackListener" callback="true" /></dubbo:method></dubbo:service>
</fakeRoot>
2021-12-09 17:32:02
是处理引用了多个注册中心bean的情况,详细参考3.2.5:引用多个注册中心
。2021-12-10 10:22:22
处是处理事件通知 相关属性,可能配置如下:
<fakeRoot><dubbo:reference id="myEventNotifyServiceInConsumerSide"interface="dongshi.daddy.service.MyEventNotifyService"><dubbo:method name="getUserName"onreturn="consumerNotifyServiceImpl.onReturn"oninvoke="consumerNotifyServiceImpl.onInvoke"onthrow="consumerNotifyServiceImpl.onThrow"/></dubbo:reference><bean class="dongshi.daddy.service.eventynotify.ConsumerNotifyServiceImpl" id="consumerNotifyServiceImpl"/>
</fakeRoot>
格式是beanId.方法名称
,代码解析都是通过.号分割后获取对应的spring bean ID和方法名称,然后存储到bean定义中。
详细参考3.2.4:parseArguments
。
3.2.1:dubbo:protocol
代码片段如下:
class FakeCls {void fakeMethod() {// 处理在<dubbo:service>中配置了protocol属性的情况,简单来说就是将自己设置到依赖于自己的bean定义中if (ProtocolConfig.class.equals(beanClass)) {for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {// 获取bean定义并获取其protocol属性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())) {// 添加当前bean定义,其实就是<dubbo:service>对应的bean定义,protocol属性为当前的<dubbo:protocol>对应的bean定义definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));}}}}}
}
3.2.2:dubbo:service
代码片段如下:
class FakeCls {void fakeMethod() {if (ProtocolConfig.class.equals(beanClass)) {// ...} else if (ServiceBean.class.equals(beanClass)) {// 获取class属性的值,配置的是服务类的实现类,如配置:<dubbo:service interface="xxx.MyService" class="xxx.MyServiceimpl" .../>// 也可配置ref属性来引用实现类对应的spring beanString className = element.getAttribute("class");if (className != null && className.length() > 0) {// 创建封装实现类的bean定义RootBeanDefinition classDefinition = new RootBeanDefinition();// 设置spring bean classclassDefinition.setBeanClass(ReflectUtils.forName(className));// 设置为饥饿加载,即直接加载classDefinition.setLazyInit(false);// 2021-12-08 14:48:29// 解析<property>子标签,该子标签用于配置class的属性值,源码如下:parseProperties(element.getChildNodes(), classDefinition);// 添加ref属性,实现类的id默认使用service ID + ”Impl“,如service ID=myService,则结果就是myServiceImplbeanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));}}}
}
2021-12-08 14:48:29
处是处理property子标签,即服务提供实现类的属性配置,源码如下:
class FakeCls {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=\"...\" />");}}}}}}}
}
3.2.3:dubbo:provider
代码片段如下:
class FakeCls {// element:要处理的元素// parserContext:解析上下文// beanClass:要解析的子标签对应的class// tag:要解析的子标签名称// property:当前元素的标签名// ref:当前要解析的标签对应的spring bean ID// beanDefinition:当前要解析的标签对应的beandefinitionprivate 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);// 是Element才处理,忽略注释,空行等if (node instanceof Element) {if (tag.equals(node.getNodeName())|| tag.equals(node.getLocalName())) {// 处理default,不知道干啥用的,谁知道的话欢迎告知下!!!if (first) {first = false;String isDefault = element.getAttribute("default");if (isDefault == null || isDefault.length() == 0) {beanDefinition.getPropertyValues().addPropertyValue("default", "false");}}// 递归调用,获取tag子标签的bean定义对象BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);// 设置当前子bean定义指向父bean定义if (subDefinition != null && ref != null && ref.length() > 0) {subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));}}}}}}
}
3.2.4:parseArguments
如用在dubbo:method
子标签中,设置方法的参数信息,源码如下:
class FakeCls {private static void parseArguments(String id, NodeList nodeList, RootBeanDefinition beanDefinition,ParserContext parserContext) {if (nodeList != null && nodeList.getLength() > 0) {ManagedList arguments = null;for (int i = 0; i < nodeList.getLength(); i++) {Node node = nodeList.item(i);if (node instanceof Element) {Element element = (Element) node;if ("argument".equals(node.getNodeName()) || "argument".equals(node.getLocalName())) {String argumentIndex = element.getAttribute("index");if (arguments == null) {arguments = new ManagedList();}// 创建dubbo:arguemnt对应的ArgumentConfig的beandefinition对象,并设置其idBeanDefinition argumentBeanDefinition = parse(((Element) node),parserContext, ArgumentConfig.class, false);String name = id + "." + argumentIndex;BeanDefinitionHolder argumentBeanDefinitionHolder = new BeanDefinitionHolder(argumentBeanDefinition, name);// 添加到结果集合中arguments.add(argumentBeanDefinitionHolder);}}}if (arguments != null) {// 添加到父bean定义中,如/*<dubbo:method ...><dubbo:argument .../><dubbo:argument .../><dubbo:argument .../></dubbo:method>*/// 就是添加到<dubbo:method>对应的beandefinition中beanDefinition.getPropertyValues().addPropertyValue("arguments", arguments);}}}
}
3.2.5:引用多个注册中心
源码如下:
// com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser.parseMultiRef
class FakeCls {// property:注册到bean定义中的属性键// value:引用的多个配置的值,逗号分隔// beanDefinition:当前解析的标签对应的bean定义// parserContext:解析上下文private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition,ParserContext parserContext) {String[] values = value.split("\\s*[,]+\\s*");ManagedList list = null;// 循环添加到RuntimeBeanReference中,在后续流程中解析for (int i = 0; i < values.length; i++) {String v = values[i];if (v != null && v.length() > 0) {if (list == null) {list = new ManagedList();}list.add(new RuntimeBeanReference(v));}}// 添加到bean定义中beanDefinition.getPropertyValues().addPropertyValue(property, list);}
}