-
spring启动解析dubbo的bean定义
在dubbo-config-spring 的resources/META-INF/spring.handlers文件中,指定了dubbo的配置解析器, 用来解析dubbo配置文件中的那些标签。key是spring配置文件中的schemaLocation指定的,value是解析器类,一一对应。
spring容器启动阶段加载配置文件中bean定义的时候(org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)中),会根据标签的namespaceUri获取对应的NamespaceHandler,再用handler解析xml形式的bean定义。
public class BeanDefinitionParserDelegate { // ....省略无关代码 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = this.getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } else { return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } } }
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
中, 如果首次加载handlerMappings,会扫描所有jar包下的META-INF/spring.handlers,保存在DefaultNamespaceHandlerResolver的Map<String, Object> handlerMappings中。
spring.handlers
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
dubbo-spring配置文件
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
-
spring创建所有非懒加载bean,dubbo SPI自适应扩展类加载
上面已经加载了spring配置文件中dubbo相关bean定义,每种dubbo标签都会被解析为一个BeanDefintion, 例如下面这个声明服务接口的配置
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
会被解析为beanName为com.alibaba.dubbo.demo.DemoService,beanClass为com.alibaba.dubbo.config.spring.ServiceBean 的 BeanDefintion,而ServiceBean的继承关系如下,
package com.alibaba.dubbo.config.spring; public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware { //... }
其中ServiceConfig的静态属性有一句
public class ServiceConfig<T> extends AbstractServiceConfig { private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); // ......
在spring容器启动阶段创建所有非懒加载bean(finishBeanFactoryInitialization(beanFactory) 方法中)时,静态属性初始化,执行getAdaptiveExtension() 方法会加载所有 @SPI注解的类,并缓存起来。诸如此类的加载的类还有Protocol、Cluster、LoadBalance 等。这样就和源码导读里的 Dubbo SPI串联起来了。
http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html
总的来说Dubbo SPI的流程就是: 根据指定类型的完整包路径,拼装SPI配置文件路径,加载这些路径指定的文件中,文件的键为需要加载的类的名称(duboo中的url携带的参数),值为这个类的位置,反射创建类后缓存。
例如加载协议时,类型为com.alibaba.dubbo.rpc.Protocol,拼装后的SPI配置文件路径有三个
META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol META-INF/services/com.alibaba.dubbo.rpc.Protocol
加载到这三个路径下所有键值对,假如我们url中指定的协议类型是dubbo,那就反射创建值对应的类作为协议实现类。
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
**既然SPI已经可以加载指定的类,为什么还要自适应扩展:**有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。通过自适应类代理,真正调用方法的时候再去使用SPI加载真正实现类。这就需要生成代理类代码。
http://dubbo.apache.org/zh-cn/docs/source_code_guide/adaptive-extension.html
-
创建bean后设置bean的非静态属性
上面ServiceBean的静态属性已经加载完了,接下来就是非静态属性的初始化。
首先是调用bean的各种*Aware接口,BeanNameAware设备bean名称,ApplicationContextAware设置this.applicationContext属性,并且反射调用org.springframework.context.support.AbstractApplicationContext#addApplicationListener方法,把ServiceBean作为一个ApplicationListener添加到spring容器的监听器列表中,ServiceBean已经实现了ApplicationListener接口,可以作为一个容器事件的监听器。
public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; SpringExtensionFactory.addApplicationContext(applicationContext); // ....省略 Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // backward compatibility to spring 2.0.1 method.invoke(applicationContext, new Object[]{this}); // supportedApplicationListener 置为 true, 这个变量在服务导出的时候会用做是否导出服务的条件。ServiceBean 是 Dubbo 与 Spring 框架进行整合的关键,可以看做是两个框架之间的桥梁。 supportedApplicationListener = true; // ....省略 }
接下来afterPropertiesSet中,把dubbo配置文件中其余的配置信息解析出来,设置到ServiceBean(继承自AbstractInterfaceConfig)的属性上。包括<dubbo:application name=“demo-provider”/>对应的ApplicationConfig,<dubbo:registry address=“multicast://224.5.6.7:1234”/>对应的RegistryConfig,<dubbo:protocol name=“dubbo” port=“20880”/>对应的ProtocolConfig等。
// 省略了很多代码 public void afterPropertiesSet() throws Exception { if (getApplication() == null && (getProvider() == null || getProvider().getApplication() == null)) { setApplication(applicationConfig); } if (getModule() == null && (getProvider() == null || getProvider().getModule() == null)) { setModule(moduleConfig); } if ((getRegistries() == null || getRegistries().size() == 0) && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0) && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) { super.setRegistries(registryConfigs); } if (getMonitor() == null && (getProvider() == null || getProvider().getMonitor() == null) && (getApplication() == null || getApplication().getMonitor() == null)) { setMonitor(monitorConfig); } if ((getProtocols() == null || getProtocols().size() == 0) && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) { super.setProtocols(protocolConfigs); } }
-
spring容器启动bean创建完后发布容器刷新事件,准备dubbo服务导出
spring容器启动bean创建完后发布容器刷新事件,org.springframework.context.support.AbstractApplicationContext#finishRefresh
protected void finishRefresh() { // .... this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this))); // .... }
ServiceBean 作为一个事件监听器,在上面3中已经加入spring容器的监听器列表中。
onApplicationEvent方法收到事件后,判断如果不是延迟导出服务,并且还没有导出,则调用了export(); 方法导出生产者服务。
public void onApplicationEvent(ContextRefreshedEvent event) { if (isDelay() && !isExported() && !isUnexported()) { // ..... export(); } }
如此便进入服务导出的逻辑,http://dubbo.apache.org/zh-cn/docs/source_code_guide/export-service.html
调试dubbo-1
本文详细解释了Spring如何解析Dubbo配置文件中的bean定义,介绍了Dubbo SPI的加载过程,以及自适应扩展在服务导出中的应用。重点涉及NamespaceHandler、SPI类加载和ServiceBean初始化。
摘要由CSDN通过智能技术生成