功能概述
- Dubbo框架会集成Spring的能力,在以XML方式配置信息时,会用自定义的处理器DubboNamespaceHandler和解析器DubboBeanDefinitionParser对XML进行解析,生成对应的Bean对象和Config对象。
功能分析
DubboBeanDefinitionParser类分析
主要成员变量分析
private final Class<?> beanClass; // Bean对应的Class类(即Config的Class类)
private final boolean required; // 是否必须(即Config类的id是否必须)
主要成员方法分析
解析XML元素
private static RootBeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { //ParserContext:通过bean定义解析过程传递的上下文,封装所有相关配置和状态,嵌套在XmlReaderContext内RootBeanDefinition beanDefinition = new RootBeanDefinition(); //创建spring的Bean实例beanDefinition.setBeanClass(beanClass); //设置Bean对应的class类,如MethodConfig.classbeanDefinition.setLazyInit(false); //设置是否延迟初始化,false:在spring容器启动时,就会创建Bean实例String id = resolveAttribute(element, "id", parserContext); //解析属性id的值/*** 处理属性id的值,若属性id为空且是必须的,则尝试获取name、interface属性对应的值,若还为空则获取bean的名称*/if (StringUtils.isEmpty(id) && required) { //未设置属性id时,使用其它属性产生id值String generatedBeanName = resolveAttribute(element, "name", parserContext); //解析属性name的值if (StringUtils.isEmpty(generatedBeanName)) {if (ProtocolConfig.class.equals(beanClass)) {generatedBeanName = "dubbo";} else {generatedBeanName = resolveAttribute(element, "interface", parserContext);}}if (StringUtils.isEmpty(generatedBeanName)) {generatedBeanName = beanClass.getName(); //bean的名称如:class org.apache.dubbo.config.ApplicationConfig}id = generatedBeanName;int counter = 2;while (parserContext.getRegistry().containsBeanDefinition(id)) { //若bean已存在,则加上计数标识,直到没有重复的id为止id = generatedBeanName + (counter++); // count++的值从2开始递增,变量count在前,count++就是加之前的值}}if (StringUtils.isNotEmpty(id)) {if (parserContext.getRegistry().containsBeanDefinition(id)) { //判断已经注册的Bean中是否有重复的idthrow new IllegalStateException("Duplicate spring bean id " + id);}parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); // 向spring注册中心注册bean实例(将id作为Bean的名称)beanDefinition.getPropertyValues().addPropertyValue("id", id); // 设置bean的id属性}/*** 对指定的bean进行处理,如ProtocolConfig、ServiceBean、ProviderConfig、ConsumerConfig等*/if (ProtocolConfig.class.equals(beanClass)) {for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { //遍历已注册的bean对应的名称列表BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); //获取指定bean名称对应的BeanDefinition实例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 = resolveAttribute(element, "class", parserContext);if (StringUtils.isNotEmpty(className)) { //在<dubbo:service/>中设置class属性时进入RootBeanDefinition classDefinition = new RootBeanDefinition(); //根bean定义也可以用于注册单个bean定义classDefinition.setBeanClass(ReflectUtils.forName(className));classDefinition.setLazyInit(false);parseProperties(element.getChildNodes(), classDefinition, parserContext);beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); //BeanDefinitionHolder:带有名称和别名的bean定义的Holder// 此处的bean实例,会在bean的名称后面加上Impl,如:org.apache.dubbo.demo.DemoService + "Impl",对应的实现类是org.apache.dubbo.demo.DemoServiceImpl}} else if (ProviderConfig.class.equals(beanClass)) { //对<dubbo:provider> 中的嵌套<dubbo:service> 元素进行解析parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);} else if (ConsumerConfig.class.equals(beanClass)) { //对<dubbo:consumer> 中的嵌套<dubbo:reference> 元素进行解析parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);}Set<String> props = new HashSet<>();ManagedMap parameters = null; //托管的Map(Spring的标签集合类,用于保存被托管的Map)for (Method setter : beanClass.getMethods()) { //遍历Config中的set方法提取出属性名,然后通过XML的Element解析出对应的属性值,最后依次设置到Config类关联的Bean的属性中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 beanProperty = name.substring(3, 4).toLowerCase() + name.substring(4); //解析出属性名,如方法名为setName,属性名为nameString property = StringUtils.camelToSplitName(beanProperty, "-"); //按分隔符方式处理属性名称props.add(property); //将属性加到属性集合中// check the setter/getter whether match (检查set、get方法是否匹配)Method getter = null;try {getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);//获取指定的属性对应的get方法,如getName} catch (NoSuchMethodException e) {try {getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]); //处理is开头的方法,比如ApplicationConfig中的isDefault()方法} catch (NoSuchMethodException e2) { //允许没有get方法,比如EnvironmentAware,只有set方法,不能抛出异常,不然引起应用启动失败// ignore, there is no need any log here since some class implement the interface: EnvironmentAware,// ApplicationAware, etc. They only have setter method, otherwise will cause the error log during application start up.}}if (getter == null|| !Modifier.isPublic(getter.getModifiers())|| !type.equals(getter.getReturnType())) { //若没有找到符合条件的get方法,则本次不处理,跳到下次循环continue;}/*** 从XML元素中读取Config类属性对应的值,并设置到Bean的属性中*/if ("parameters".equals(property)) {parameters = parseParameters(element.getChildNodes(), beanDefinition, parserContext); //解析<dubbo:parameter/>元素的值} else if ("methods".equals(property)) {parseMethods(id, element.getChildNodes(), beanDefinition, parserContext); //解析<dubbo:method/>元素的值} else if ("arguments".equals(property)) {parseArguments(id, element.getChildNodes(), beanDefinition, parserContext); //解析<dubbo:argument/>元素的值} else { //对常规属性做处理String value = resolveAttribute(element, property, parserContext); //解析XML中元素对应的属性值if (value != null) { //若属性值为不为空,则对应处理(即XML中设置了对应的属性值)value = value.trim(); //过滤字符串的前后空格(即过滤掉空字符串)if (value.length() > 0) {if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {RegistryConfig registryConfig = new RegistryConfig();registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);beanDefinition.getPropertyValues().addPropertyValue(beanProperty, registryConfig);} else if ("provider".equals(property) || "registry".equals(property) || ("protocol".equals(property) && AbstractServiceConfig.class.isAssignableFrom(beanClass))) {// Class中方法isAssignableFrom():判断当前class是否与参数中指定的class相同,或者是参数指定的class的父类或父接口(主语是当前class,如上的主语即为:AbstractServiceConfig)beanDefinition.getPropertyValues().addPropertyValue(beanProperty + "Ids", value);} else {Object reference;if (isPrimitive(type)) { //对基本类型的属性处理(基本类型进行了扩展,包含String、Date等)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 xsd(向后兼容旧版本的XSD中的默认值)value = null; //若旧版本的属性满足条件,则将属性值设置为null}reference = value;// 对方法<dubbo:method>元素中的onreturn、onthrow、oninvoke属性进行处理} else if (ONRETURN.equals(property) || ONTHROW.equals(property) || ONINVOKE.equals(property)) {int index = value.lastIndexOf("."); //待覆盖调试:事件通知允许Consumer端在调用之前、调用之后或出现异常时,触发oninvoke、onreturn、onthrow三个事件。 https://dubbo.apache.org/zh/docs/advanced/events-notify/String ref = value.substring(0, index); //onreturn、onthrow、oninvoke的属性值,必须要以 xxx.方法名形式,若没有".",则会由于[0,-1)报字符串区间错误 "String index out of range: -1"String method = value.substring(index + 1);reference = new RuntimeBeanReference(ref);beanDefinition.getPropertyValues().addPropertyValue(property + METHOD, method);} else { //解析ref属性<property name="ref">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); //创建指定名称的bean}beanDefinition.getPropertyValues().addPropertyValue(beanProperty, reference); //为bean添加属性值}}}}}}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的parameters属性值beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);}return beanDefinition;
}
- 代码解析:
- 代码中的RootBeanDefinition时Spring的Bean,可以作为其他BeanDefinition的父BeanDefinition,也可以单独作为BeanDefinition,但是不能作为其他BeanDefinition的子BeanDefinition。
- 代码中的NamedNodeMap代表一个节点的无序列表,可以通过它们的名称进行访问。
解析内嵌元素
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) {return;}boolean first = true;for (int i = 0; i < nodeList.getLength(); i++) { //可以有多个内嵌节点Node node = nodeList.item(i);if (!(node instanceof Element)) {continue;}if (tag.equals(node.getNodeName()) //带上命名空间的节点名称,如dubbo:service(按指定的tag查找Node)|| tag.equals(node.getLocalName())) { //去掉命名空间的名称,如serviceif (first) { //有多个元素时,如<dubbo:service>,只有第一个元素才处理default值first = false; //处理好第一个元素后,更改该标志String isDefault = resolveAttribute(element, "default", parserContext); // default: 是否为缺省协议,用于多协议if (StringUtils.isEmpty(isDefault)) { //处理默认属性defaultbeanDefinition.getPropertyValues().addPropertyValue("default", "false");}}// 内部嵌套的元素,按单个元素解析的方式依次解析BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required); //获取嵌套元素对应的Bean,如<dubbo:provider>中<dubbo:service>if (subDefinition != null && StringUtils.isNotEmpty(ref)) { //依赖的bean用RuntimeBeanReference表示subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));}}}
}
- 代码解析:
- BeanDefinition:在Spring中,Bean的解析阶段,会把xml配制中的标签解析成Spring中的BeanDefinition对象
- RuntimeBeanReference:如果一个bean依赖其它的bean,比如dubbo:service中ref,那么被依赖的bean就用RuntimeBeanReference表示(因为解析阶段,还没有依赖的bean的实例,等解析以后存在实例时,再根据RuntimeBeanReference关联)
解析元素属性值
private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) {if (nodeList == null) {return;}for (int i = 0; i < nodeList.getLength(); i++) { //NodeList: 提供了有序节点的抽象节点的集合if (!(nodeList.item(i) instanceof Element)) {continue;}Element element = (Element) nodeList.item(i);if ("property".equals(element.getNodeName()) //只对属性节点处理<property>|| "property".equals(element.getLocalName())) {String name = resolveAttribute(element, "name", parserContext); //解析<property>中的name属性if (StringUtils.isNotEmpty(name)) {String value = resolveAttribute(element, "value", parserContext);String ref = resolveAttribute(element, "ref", parserContext);if (StringUtils.isNotEmpty(value)) { //解析基本属性beanDefinition.getPropertyValues().addPropertyValue(name, value); //属性值列表beanDefinition.getPropertyValues(), 对应<property>的值} else if (StringUtils.isNotEmpty(ref)) { //解析含有引用的属性,创建对应的beanbeanDefinition.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=\"...\" />");}}}}
}
解析参数标签
private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) {if (nodeList == null) { //NodeList表示一个有顺序的节点列表return null;}ManagedMap parameters = null; //托管的Map,用来保存map的值for (int i = 0; i < nodeList.getLength(); i++) { //可以有多个元素,如:<dubbo:parameter>if (!(nodeList.item(i) instanceof Element)) { //若元素不是Element实例,则不处理continue;}Element element = (Element) nodeList.item(i);if ("parameter".equals(element.getNodeName())|| "parameter".equals(element.getLocalName())) { //只处理<dubbo:parameter/>if (parameters == null) {parameters = new ManagedMap();}String key = resolveAttribute(element, "key", parserContext); //解析<dubbo:parameter> 元素中的keyString value = resolveAttribute(element, "value", parserContext); //解析<dubbo:parameter> 元素中的valueboolean hide = "true".equals(resolveAttribute(element, "hide", parserContext));if (hide) { //是否隐藏,若需要隐藏,加上前缀key = HIDE_KEY_PREFIX + key;}parameters.put(key, new TypedStringValue(value, String.class)); //多个 <dubbo:parameter>时,若key重复,value值会被覆盖(TypedStringValue:存储了string值,已经要转换的目标类型,类型转换由bean工厂处理)}}return parameters; //此处没有直接添加到bean的属性中,而是在DubboBeanDefinitionParser#parse方法的最后添加的
}
DubboNamespaceHandler类分析
主要成员方法分析
初始化方法
public void init() { //在解析自定义元素前,进行初始化操作(设置父类NamespaceHandlerSupport的Map<String, BeanDefinitionParser> parsers,即设置元素名与bean解析器的映射)registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); //将元素名与对应的bean进行对应registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); //对应注解解析器
}
- 代码解析:
- init()方法解析:
- 重写Spring NamespaceHandler的init()方法,
- 在解析XML中的命名空间url时,如xmlns:dubbo="http://dubbo.apache.org/schema/dubbo",会调用init()方法,
- 调用的地方org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve
- registerBeanDefinitionParser()方法解析:
- 重写Spring NamespaceHandlerSupport#registerBeanDefinitionParser()方法
- 将元素名,如"application"与对应的解析器按键值对存储起来 Map<String, BeanDefinitionParser> parsers
- 注册以后当前对象DubboNamespaceHandler从NamespaceHandlerSupport继承的私有成员变量parsers就有相关值了
- init()方法解析:
问题点答疑
-
DubboBeanUtils#registerCommonBeans功能用途是什么?
- 解答:用来注册含有基本功能的bean,如DubboBootstrapApplicationListener、ReferenceAnnotationBeanPostProcessor等等
会打印出"The Infrastructure bean definition"日志
- 解答:用来注册含有基本功能的bean,如DubboBootstrapApplicationListener、ReferenceAnnotationBeanPostProcessor等等
-
是怎样加载到自定义的DubboNamespaceHandler的?
- 解答:当Spring读取XML时,遇到非Spring定义XML元素时,就会查找spring.schemas和spring.handlers,在spring.handlers中配置了DubboNamespaceHandler处理器。
- 解答:当Spring读取XML时,遇到非Spring定义XML元素时,就会查找spring.schemas和spring.handlers,在spring.handlers中配置了DubboNamespaceHandler处理器。
归纳总结
-
XML关联信息介绍
- XML指可扩展标记语言、被设计用来传输和存储数据。HTML被设计用来显示数据
- 第一行是XML声明,XML文档必须包含根元素(有且只有一个根元素)。该元素是所有其他元素的父元素。XML文档形成一种树结构。
- XML元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。元素可包含其他元素、文本或者两者的混合物。元素也可以拥有属性。
- 拥有正确语法的XML被称为“形式良好”的XML。可以通过DTD、Schema验证的XML是否是“合法”的XML。
- 解析器把XML转换为XML DOM对象,XML DOM (XML Document Object Model) 定义了访问和操作XML文档的标准方法。
- XML命名空间:提供避免元素命名冲突的方法。所有 XML 文档中的文本均会被解析器解析。只有 CDATA 区段(CDATA section)中的文本会被解析器忽略
-
DOM中的节点Node介绍:
- XML 文档中的每个成分都是一个节点。
- DOM 对应的规定:
- 整个文档是一个文档节点
- 每个 XML 标签是一个元素节点
- 包含在 XML 元素中的文本是文本节点
- 每一个 XML 属性是一个属性节点
- 注释属于注释节点