功能概述
- Dubbo框架会集成Spring的能力,在以XML方式配置信息时,会用自定义的处理器DubboNamespaceHandler和解析器DubboBeanDefinitionParser对XML进行解析,生成对应的Bean对象和Config对象。
功能分析
DubboBeanDefinitionParser类分析
主要成员变量分析
java
private final Class<?> beanClass; // Bean对应的Class类(即Config的Class类)
private final boolean required; // 是否必须(即Config类的id是否必须)
主要成员方法分析
解析XML元素
java
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.class
beanDefinition.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中是否有重复的id
throw 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,属性名为name
String 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代表一个节点的无序列表,可以通过它们的名称进行访问。
解析内嵌元素
java
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())) { //去掉命名空间的名称,如service
if (first) { //有多个元素时,如<dubbo:service>,只有第一个元素才处理default值
first = false; //处理好第一个元素后,更改该标志
String isDefault = resolveAttribute(element, "default", parserContext); // default: 是否为缺省协议,用于多协议
if (StringUtils.isEmpty(isDefault)) { //处理默认属性default
beanDefinition.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关联)
解析元素属性值
java
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)) { //解析含有引用的属性,创建对应的bean
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=\"...\" />");
}
}
}
}
}
解析参数标签
java
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> 元素中的key
String value = resolveAttribute(element, "value", parserContext); //解析<dubbo:parameter> 元素中的value
boolean 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类分析
主要成员方法分析
初始化方法
java
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处理器。
归纳总结
-
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 属性是一个属性节点
- 注释属于注释节点