dubbo源码解析(一)
标签解析和bean的初始化
首先找到程序入口,在dubbo-demo下的dubbo-demo-consumer中的consumer文件中:

进入查看代码发现依赖了一个配置类,META-INF/spring/dubbo-demo-consumer.xml。
public static void main(String[] args) {
//Prevent to get IPV6 address,this way only work in debug mode
//But you can pass use -Djava.net.preferIPv4Stack=true,then it work well whether in debug mode or not
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
context.start();
DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy
while (true) {
try {
Thread.sleep(1000);
String hello = demoService.sayHello("world"); // call remote method
System.out.println(hello); // get result
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
追踪META-INF/spring/dubbo-demo-consumer.xml这个配置类,我们发现它引用的xsd文件包括spring的spring-beans-4.3.xsd文件和dubbo.xsd,dubbo.xsd文件定义了xml文件的解析规则。例如看dubbo: reference,在xsd文件中可以看到reference对应的配置,对应ReferenceConfig类。**所以我们现在要追踪的是:
dubbo.xsd
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- consumer's application name, used for tracing dependency relationship (not a matching criterion),
don't set it same as provider -->
<dubbo:application name="dub-consumer"/>
<!-- use multicast registry center to discover service -->
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<!-- generate proxy for the remote service, then demoService can be used in the same way as the
local regular interface -->
<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
</beans>
ctrl+鼠标左键进入到dubbo.xsd,这里主要查看
注:这里的<xsd:annotation>使用DubboBeanDefinitionParser方法转译成的bean,所以看不懂,往下看即可。
service
reference
<xsd:element name="service" type="serviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="reference" type="referenceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Reference service config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
找到dubbo.xsd的上级目录下的spring.handles:
因为handles这里配置了DubboNamespaceHandler,进入这个类查看:可以发现这个类使用了DubboBeanDefinitionParser帮我们解析上面的标签,并且自定义了ServiceBean和ReferenceBean。
http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
内部机制都是依托于<dubbo:annotation />标签。通过源码分析,Dubbo对于Spring xml解析处理由 com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler提供:DubboNamespaceHandler.java
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@Override
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());
}
}
这里不急着看DubboBeanDefinitionParser,我们先来看一下registerBeanDefinitionParser这个类所需的元素,可以得到两个信息
从这里我们可以看出DubboBeanDefinitionParser最终是返回了一个BeanDefinitionParser的类或者子类出来:
elementName:传入元素名
BeanDefinitionParser:传入解析后的bean
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
点击进入DubboBeanDefinitionParser,可以发现:
element:表示 HTML 或 XML 文档中的元素。元素可能有与之关联的属性
beanclass:要被解析成的配置类(或者叫做,空的实体类)
required:是否进行解析
beanclass:要被解析成的配置类(或者叫做,空的实体类)
required:是否进行解析
DubboBeanDefinitionParser是一个构造函数,会根据传入的beanclass和required
pares方法调用了内部方法parse,介绍一下传入参数:
public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
this.beanClass = beanClass;
this.required = required;
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
return parse(element, parserContext, beanClass, required);
}
下面跟进parse方法,查看发生了什么:
element:表示 HTML 或 XML 文档中的元素。元素可能有与之关联的属性
parserContext:解析外部命名空间的bean,所需要用到的类。
beanClass:要被解析成的配置类(或者叫做,空的实体类)
required:如果id为空,则进行解析,解析成xml里的name、interface、或者bean的名字
解释一下传入参数
@SuppressWarnings("unchecked")
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
//创建一个RootBeanDefinition,作为要返回的类
RootBeanDefinition beanDefinition = new RootBeanDefinition();
//把我们需要注入的beanclass放入
beanDefinition.setBeanClass(beanClass);
//不设置懒加载
beanDefinition.setLazyInit(false);
//从xml里面获取id
String id = element.getAttribute("id");//(*)
//未获取到id或id为空,且是否需要解析为需要
if ((id == null || id.length() == 0) && required) {
//进行id的解析,根据结点属性解析成xml的标签属性或处理结点名#
//从xml里获取bean的name
String generatedBeanName = element.getAttribute("name");
if (generatedBeanName == null || generatedBeanName.length() == 0) {
//若为protocol处理结点,且name为空,则默认为 "dubbo"
if (ProtocolConfig.class.equals(beanClass)) {
generatedBeanName = "dubbo";
} else {
//如果是普通结点则为其配置成interface的值
generatedBeanName = element.getAttribute("interface");
}
}
if (generatedBeanName == null || generatedBeanName.length() == 0) {
//如果id不为空,但generatedBeanName为空,则设置成对应的class名字
generatedBeanName = beanClass.getName();
}
id = generatedBeanName;//将id改为置入的bean名(*)
int counter = 2; //初始化counter为2
while (parserContext.getRegistry().containsBeanDefinition(id)) {
//为防止出现id重复,从parserContext中获取bean
// 判断是否重复,重复则将generatedBeanName和counter进行拼串
//重复则进行循环拼串,拼到不重复为止
id = generatedBeanName + (counter++);//进行一个拼串(*)
}
}
if (id != null && id.length() > 0) {//将id写入容器中
//由于传入的required为true,所以这个位置必定会进入#
//判断传入的id是否包含在parserContext中
//包含则抛出异常
if (parserContext.getRegistry().containsBeanDefinition(id)) {
throw new IllegalStateException("Duplicate spring bean id " + id);
}
//注册id-beandefinetion到容器中
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
//向我们要返回的beanDefinition传入id,前面一切的目的都是为了这一步,向里面放入id(*)
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
// <dubbo:protocol/>标签处理
if (ProtocolConfig.class.equals(beanClass)) { //如果是为protocol处理结点
for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {//迭代获取bean的名字
BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);//从拿到的名字里获取bean
PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");//获取protocol处理结点
if (property != null) {//如果结点不为空
Object value = property.getValue();//从处理结点中获取对象
if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
//如果value是ProtocolConfig的实体类或者子类,并且id的值和ProtocolConfig的妹子相同
//获取知识点:instanceof,表示左边的是否为右边的实体类或子类
//则为其添加对当前bean id的依赖
definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
}
}
}
// <dubbo:service/>标签
} else if (ServiceBean.class.equals(beanClass)) {//如果是service
//从标签中获取bean
String className = element.getAttribute("class");
//如果存在这个bean
if (className != null && className.length() > 0) {
// 构建配置的class的BeanDefinition
RootBeanDefinition classDefinition = new RootBeanDefinition();
// 设置beanClass
classDefinition.setBeanClass(ReflectUtils.forName(className));
//不设置懒加载
classDefinition.setLazyInit(false);
//解析<property/>子标签
parseProperties(element.getChildNodes(), classDefinition);
//添加ServiceBean ref属性的依赖
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
}
// <dubbo:provider/>标签
} else if (ProviderConfig.class.equals(beanClass)) {
//解析嵌套元素,parseNested方法后面会讲。
parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
// <dubbo:consumer/>标签
} else if (ConsumerConfig.class.equals(beanClass)) {
//解析嵌套元素
parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
}
Set<String> props = new HashSet<String>();//初始化一个hashset名为props
ManagedMap parameters = null;//初始化一个Managedmap
for (Method setter : beanClass.getMethods()) {//遍历beanclass的方法
String name = setter.getName();//获取方法名
//判断是否是public的有参数的setter方法
if (name.length() > 3 && name.startsWith("set")
&& Modifier.isPublic(setter.getModifiers())
&& setter.getParameterTypes().length == 1) {
//获取参数类型
Class<?> type = setter.getParameterTypes()[0];
// 将驼峰命名去除改成由-连接,如setApplicationContext --> application-context
String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
//加入我们定义的hashset中
props.add(property);
Method getter = null;//初始化一个方法命名为getter方法
try {
//获取以get开头的方法
getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
} catch (NoSuchMethodException e) { //捕获没有这个方法的异常
try {
//获取以is开头的方法
getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
} catch (NoSuchMethodException e2) {//捕获没有这个方法的异常
}
}
//如果获取方法为空,或者方法不为公共,又或者setter方法的类型和getter方法返回类型不同,则退出循环
// 注:type是setter方法的传入参数类型
if (getter == null
|| !Modifier.isPublic(getter.getModifiers())
|| !type.equals(getter.getReturnType())) {
continue;
}
if ("parameters".equals(property)) {
/* parameters属性解析 */
parameters = parseParameters(element.getChildNodes(), beanDefinition);
} else if ("methods".equals(property)) {
/* methods属性解析 */
parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
} else if ("arguments".equals(property)) {
/* arguments属性解析 */
parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
} else {
//如果都不是,则从标签里获取标签名为property的值
String value = element.getAttribute(property);
if (value != null) {
//值不为空,则去除值的头部和尾部的空字符
value = value.trim();
if (value.length() > 0) { //如果字符长度大于1
//如果property的值是registry,并且忽略大小写它的值是为N/A
if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
RegistryConfig registryConfig = new RegistryConfig();
//创建registryConfig,将其地址设置为N/A
registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
//将registryConfig设置进要返回的beanDefinition中
beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
//如果property不为registry,且不含有','
} else if ("registry".equals(property) && value.indexOf(',') != -1) {
//遍历设置成registries,也可以跟进查看一下parseMultiRef方法
parseMultiRef("registries", value, beanDefinition, parserContext);
} else if ("provider".equals(property) && value.indexOf(',') != -1) {
//遍历设置成providers
parseMultiRef("providers", value, beanDefinition, parserContext);
} else if ("protocol".equals(property) && value.indexOf(',') != -1) {
//遍历设置成protocols
parseMultiRef("protocols", value, beanDefinition, parserContext);
} else {
Object reference;
//判断是否为基本数据类型和包装类型,类似Long,String,Short等
//以下的代码全部都是为了版本兼容,看到这里就差不多了,当然也可以继续看
if (isPrimitive(type)) {
// 向后兼容旧版本的xsd中的默认值,因为每个版本的xsd都配有默认值,例如
// <xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">
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
value = null;
}
reference = value;
} else if ("protocol".equals(property)
//如果属性为 protocol 那么要判断protocol对应的拓展点配置有没有
&& 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())) {
//标签的值如果等于dubbo:provider,则输出警告
logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
}
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName(value);
// 兼容旧版本配置
reference = protocol;
} else if ("onreturn".equals(property)) {
//回调方法 类似onSuccess
int index = value.lastIndexOf(".");
// bean的名字
String returnRef = value.substring(0, index);
String returnMethod = value.substring(index + 1);
reference = new RuntimeBeanReference(returnRef);
// 添加onreturnMethod属性值
beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
} else if ("onthrow".equals(property)) {
//回调 异常执行的方法 ,类似 onError
int index = value.lastIndexOf(".");
String throwRef = value.substring(0, index);
String throwMethod = value.substring(index + 1);
//获取ref和方法,将ref放入RuntimeBeanReference中
reference = new RuntimeBeanReference(throwRef);
//向我们自定义类里添加onthrowMethod的值
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);
//获取ref和方法,将ref放入RuntimeBeanReference中
reference = new RuntimeBeanReference(invokeRef);
//向我们自定义类里添加oninvokeMethod的值
beanDefinition.getPropertyValues().addPropertyValue("oninvokeMethod", invokeRefMethod);
}else {
//如果以上都没进行操作,并且property的字符等于ref,容器包含它的值,
if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
//从ref里面获取这个bean
BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
/*
必须是单例bean(singleton),原型bean(prototype)不行,sevice初始化一次,在spring容器里也只有一个 实例
是不是和dubbo的幂等有关,如果为原型bean,那么服务就变成有状态的了
*/
if (!refBean.isSingleton()) {//如果不是单例的bean
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
RuntimeBeanReference 固定占位符类,当在beanfactory中作为另外一个bean的引用时,作为属性值对象,将在运行时进行解析
*/
beanDefinition.getPropertyValues().addPropertyValue(property, reference);
}
}
}
}
}
}
NamedNodeMap attributes = element.getAttributes();
//获取解析完后的下一个元素,就是还没被解析的元素
//没见过这样的map,试了一下里面存储类Node,Node是一个包含很多信息的结点。
int len = attributes.getLength();
//遍历获取attributes
for (int i = 0; i < len; i++) {
//获取第i个索引对应节点
Node node = attributes.item(i);
//获取此结点的名字
String name = node.getLocalName();
if (!props.contains(name)) {//查看props否包含name
if (parameters == null) {//如果parameters为空,则给与新的ManagedMap
parameters = new ManagedMap();
}
String value = node.getNodeValue();//获取node的值
//向parameters放入结点名字,将value转成String类型
parameters.put(name, new TypedStringValue(value, String.class));
}
}
//如果parameters不为空
if (parameters != null) {
//将获取到的元素全部添加进自定义的bean中
beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
}
//返回自定义的bean
return beanDefinition;
}
下面讲一下parse里面用到的方法,一些简单的就略过了:
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) {
//初始化first为真
boolean first = true;
for (int i = 0; i < nodeList.getLength(); i++) {
//传入索引i获取对应的结点node
Node node = nodeList.item(i);
//如果node和Element同类型或子类
if (node instanceof Element) {
//传入的标签名和结点名相同 或者 和类名相同
if (tag.equals(node.getNodeName())
|| tag.equals(node.getLocalName())) {
if (first) {
//把first设置为false
first = false;
String isDefault = element.getAttribute("default");
// 如果第一个子节点default属性为null,则设置为false
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));
}
}
}
}
}
}
//遍历nodeList,解析出对应的方法,放入我们自定义的bean里
@SuppressWarnings("unchecked")
private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
ParserContext parserContext)
// 遍历元素,将元素存入map中,返回这个map
@SuppressWarnings("unchecked")
private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition)
至此dubbo源码分析(一):xml标签解析已经完成,下一篇讲bean的生成,又是一个长流程。




