dubbo源码系列3——dubbo自定义标签解析
Posted xu_jiang_hua
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo源码系列3——dubbo自定义标签解析相关的知识,希望对你有一定的参考价值。
在梳理完dubbo spi 机制后【dubbo源码系列1——spi源码解读(上)dubbo源码系列2——spi源码解读(下)】
本节开始梳理dubbo provider的启动流程,因基于dubbo源码进行研究,因此直接采用dubbo源码中dubbo-demo模块中xml配置示例作为demo。
本节将解析dubbo是如何利用spring进行自定义标签解析并注册到容器中,本节安排如下:
- demo
- 解析流程分析
- 总结
一、demo
- 接口
public interface DemoService
String sayHello(String name);
default CompletableFuture<String> sayHelloAsync(String name)
return CompletableFuture.completedFuture(sayHello(name));
- 接口实现
public class DemoServiceImpl implements DemoService
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name)
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
@Override
public CompletableFuture<String> sayHelloAsync(String name)
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->
return "async result";
);
return cf;
- 启动
public class ApplicationP
public static void main(String[] args) throws Exception
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
context.start();
- 配置文件
<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">
<dubbo:application name="demo-provider" metadata-type="remote" owner="xjh">
<dubbo:parameter key="mapping-type" value="metadata"/>
</dubbo:application>
<!--zk注册中心-->
<dubbo:registry id="registry1" address="zookeeper://127.0.0.1:2181"/>
<!--暴露协议以及端口-->
<dubbo:protocol name="dubbo" port="256880"/>
<!--服务bean-->
<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
<!--暴露服务-->
<dubbo:service interface="org.apache.dubbo.demo.DemoService" timeout="300000" ref="demoService" registry="registry1">
</dubbo:service>
</beans>
二、解析流程分析
1、重要组件
- dubbo.xsd
位于META-INF下面,用于校验规范dubbo标签的编写; - spring.schemas
http\\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
http\\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd
xsd规范链接与实际xsd文件存放位置映射关系
- spring.handlers
http\\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
标签命名空间与实际解析handler映射关系
2、流程分析
->AbstractApplicationContext#refresh
->AbstractApplicationContext#obtainFreshBeanFactory
->AbstractRefreshableApplicationContext#refreshBeanFactory
->AbstractXmlApplicationContext#loadBeanDefinitions
->AbstractXmlApplicationContext#loadBeanDefinitions
->AbstractBeanDefinitionReader#loadBeanDefinitions
->XmlBeanDefinitionReader#loadBeanDefinitions
->XmlBeanDefinitionReader#doLoadBeanDefinitions
->XmlBeanDefinitionReader#registerBeanDefinitions
->DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
->DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
spring加载xml并解析成beanDefinition流程比较长,本节重点关注spring如何解析自定义标签解生成beanDefintion并注册到容器。
- DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
//判断是否为默认命名空间
if (delegate.isDefaultNamespace(root))
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++)
Node node = nl.item(i);
if (node instanceof Element)
Element ele = (Element) node;
//判断节点的命名空间是否为默认的beans,如果不是则走自定义解析
if (delegate.isDefaultNamespace(ele))
parseDefaultElement(ele, delegate);
else
delegate.parseCustomElement(ele);
else
delegate.parseCustomElement(root);
比如dubbo:application节点如下:
- BeanDefinitionParserDelegate#parseCustomElement
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd)
//获取节点的命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null)
return null;
//this.readerContext.getNamespaceHandlerResolver()为DefaultNamespaceHandlerResolver
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null)
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
- DefaultNamespaceHandlerResolver#resolve
public NamespaceHandler resolve(String namespaceUri)
//加载classpath下面所有的META-INF/spring.handlers文件得到命名空间与handler的映射,如下截图所示
Map<String, Object> handlerMappings = getHandlerMappings();
//获取handler
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null)
return null;
else if (handlerOrClassName instanceof NamespaceHandler)
return (NamespaceHandler) handlerOrClassName;
else
String className = (String) handlerOrClassName;
try
//加载类
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass))
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
//实例化handler,dubbo对应为DubboNamespaceHandler
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
//执行init方法
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
catch (ClassNotFoundException ex)
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
catch (LinkageError err)
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
- DefaultNamespaceHandlerResolver#getHandlerMappings
private Map<String, Object> getHandlerMappings()
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null)
synchronized (this)
handlerMappings = this.handlerMappings;
if (handlerMappings == null)
try
//加载META-INF/spring.handlers文件
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
handlerMappings = new ConcurrentHashMap<>(mappings.size());
//复制properties到map中
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
catch (IOException ex)
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
return handlerMappings;
可以命名空间为http://dubbo.apache.org/schema/dubbo的标签应该用DubboNamespaceHandler解析;
- DubboNamespaceHandler#init
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("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));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, true));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
- DubboNamespaceHandler#parse
public BeanDefinition parse(Element element, ParserContext parserContext)
//获取注册接口
BeanDefinitionRegistry registry = parserContext.getRegistry();
registerAnnotationConfigProcessors(registry);
//注册beanProcessor,比如ReferenceAnnotationBeanPostProcessor
registerCommonBeans(registry);
//调用DubboBeanDefinitionParser的parse方法进行解析
BeanDefinition beanDefinition = super.parse(element, parserContext);
setSource(beanDefinition);
return beanDefinition;
- DubboBeanDefinitionParser#parse
private static RootBeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required)
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
//优先使用id,次优使用name,最后使用interface.如果包含相同的beanName,则通过增加后缀id来区分
String id = resolveAttribute(element, "id", parserContext);
if (StringUtils.isEmpty(id) && required)
String generatedBeanName = resolveAttribute(element, "name", parserContext);
if (StringUtils.isEmpty(generatedBeanName))
if (ProtocolConfig.class.equals(beanClass))
generatedBeanName = "dubbo";
else
generatedBeanName = resolveAttribute(element, "interface", parserContext);
if (StringUtils.isEmpty(generatedBeanName))
generatedBeanName = beanClass.getName();
id = generatedBeanName;
int counter = 2;
while (parserContext.getRegistry().containsBeanDefinition(id))
id = generatedBeanName + (counter++);
if (StringUtils.isNotEmpty(id))
if (parserContext.getRegistry().containsBeanDefinition(id))
throw new IllegalStateException("Duplicate spring bean id " + id);
//手动注册
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);
//省略部分非关键代码
可以看到通过parserContext.getRegistry().registerBeanDefinition(id, beanDefinition)将自定义schema标签解析成beanDefinition注册到beanDefinitionMap中,解析完如下所示:
三、 总结
通过自定义命名空间找到自定义NamespaceHandler,通过自定义handler解析自定义标签生成beanDefinition注册到容器中。
以上是关于dubbo源码系列3——dubbo自定义标签解析的主要内容,如果未能解决你的问题,请参考以下文章