在梳理完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注冊到容器中,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/423875.html
標籤:其他
上一篇:docker合集
下一篇:淺談智慧園區
