这里debug的代码用的是github上dubbo项目的dubbo-demo里的dubbo-demo-xml下的代码。
spring的xml文件里自定义标签的解析是基于spring.handlers文件指定的解析类来解析的,dubbo标签是在dubbo-config-spring
的jar包下的META-INF/spring.handlers
文件里。这里先看下一个consumer端的配置文件内容:
<beans
// xmlns:xsi是xsi标签命名空间
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
// xmlns:dubbo是dubbo标签的命名空间
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
// 当前那xml文件默认的命名空间
xmlns="http://www.springframework.org/schema/beans"
// xsi:schemaLocation 配置了每个命名空间对应里配置规范,用来做格式校验
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">
<!-- 当前应用名字,类似总的命名空间-->
<dubbo:application name="demo-consumer"/>
<!-- 注册中心配置-->
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="6000"/>
<!-- 协议配置-->
<dubbo:protocol name="dubbo"/>
<!-- provider配置-->
<bean id="providerService" class="org.apache.dubbo.demo.provider.ProviderServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.demo.provider.ProviderServiceImpl" ref="providerService" timeout="60000"/>
<!-- consumer配置-->
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService" timeout="6000" />
</beans>
xmlns是指定命名空间,这里可以看到dubbo的命名空间配置是xmlns:dubbo="dubbo.apache.org/schema/dubb…"
1、根据dubbo标签的命名空间获取解析类
根据dubbo标签的命名空间找到META-INF/spring.handlers
里的解析类:
这个匹配过程如图:
init()
方法负责注册每个标签对应的具体Parser
类:
public void init() {
this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
this.registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
this.registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
this.registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
this.registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));
this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
// provider端配置
this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
// consumer端配置
this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
this.registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
可以看到这里包含了dubbo标签的所有种类的解析类,比如上面配置文件里的dubbo:application、dubbo:registry、dubbo:reference注解的解析器
2、将配置解析成Beanfinition并注册到BeanFactory过程
核心逻辑在DubboBeanDefinitionParser
类的parse
方法里,先看DubboBeanDefinitionParser类的构造器:
public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
this.beanClass = beanClass;
this.required = required;
}
beanClass就是每个配置所对应的类型,由上面的init()
方法传入,比如dubbo:reference
对应ReferenceBean.class
parse
方法代码如下,由于总的代码比较长,我这里只贴出主要代码:
RootBeanDefinition beanDefinition = new RootBeanDefinition();
// 设置类型,spring容器在实例化beanDefinition时,会反射这个类的构造器实例出bean对象
beanDefinition.setBeanClass(beanClass);
...
// 将BeanDefinition注册到容器里,这个id会被作为beanName属性存在BeanDefinition里
// registerBeanDefinition的方法签名是void registerBeanDefinition(String beanName, BeanDefinition beanDefinition),所以id是作为beanName传入的
throws BeanDefinitionStoreException;
if (StringUtils.isNotEmpty(id)) {
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
...
// 遍历配置里的所有属性,比如dubbo:reference配置里的id、interface等属性,放到BeanDefinition里
NamedNodeMap attributes = element.getAttributes();
len = attributes.getLength();
for(i = 0; i < len; ++i) {
Node node = attributes.item(i);
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);
}
主要是三步:
- 设置BeanDefinition的
beanClass
属性,beanClass
由上面的init()
方法传入,比如:dubbo:reference
对应ReferenceBean.class
,spring容器在实例化beanDefinition时,会反射这个类的构造器实例出bean对象 - 通过parse方法的parserContext获取对应bean的BeanDefinitionRegistry,将BeanDefinition注册到BeanFactory里,这里有个关键点是传入的
id
会被作为beanName
设置到BeanDefinition
里,而在依赖注入时,会以beanName为key来找bean - 解析出配置的属性名字,比如consumer配置里的id、interface、group、version等信息,并将这些配置放到BeanDifinition的propertyValueList里(key为parameters),供后面实例化用。
到这里已经解析成BeanDefinition并注册到容器中,下一步就是Spring容器实例化BeanDefinition。后面会单独讲provider和consumer端怎么被实例化。