dubbo源码解析
1. ExtensionLoader、@Adaptive、@Activate
ExtensionLoader
ExtensionLoader类是dubbo的SPI机制的核心类,区别于JDK的SPI机制以及spring的SPI机制,dubbo的SPI机制提供了一些更强的功能:
- 自适应扩展
- 根据条件激活扩展
dubbo内置的扩展都在dubbo-x.x.x.jar/META-INF/dubbo.internal目录下面,如果我们想替换dubbo的组件,可以在自己项目的/META-INF/dubbo目录或者/META-INF/services目录下编写自定义的组件文件就可以了,文件名称为接口的全名称,内容为key=实现类的全名称。
ExtensionLoader.getExtension(name)
ExtensionLoader.createExtension
ExtensionLoader.getExtensionClasses
ExtensionLoader.loadExtensionClasses
//这个方法从META-INF/dubbo/、META-INF/dubbo/internal/、META-INF/services/三个目录下面加载扩展实例
ExtensionLoader.loadFile
ExtensionLoader.injectExtension
@Adaptive
通过ExtensionLoader.getAdaptiveExtension()方法获取到的自适应扩展类,打上该annotation的扩展实现类会被获取到,如果没有类被打上该annotation,则会生成一个代理类,这个代理类的方法实际上是去调用@Spi(value=xxx)指定的默认的实现类方法(或者是URL参数指定的实现类的方法,优先级更高)。有了自适应扩展标签,就不需要硬编码来指定使用哪个扩展实现类了。
自适应扩展的使用方式有两个途径:
- URL参数(优先级高)
- 添加了@Adaptive标签的扩展实现类(优先级低)
它具有以下特点:
- 如果实现类中有某一个标记了@Adaptive,则这个实现类会被使用
- 如果实现类全部都没有标记@Adaptive,则接口的方法必须有一个或多个标记@Adaptive,dubbo会动态生成一个代理类并实现打了@Adaptive标记的方法,如果方法都没有@Adaptive标签则抛错
-
动态生成xxx$Adaptive类的逻辑在
ExtensionLoader.createAdaptiveExtensionClassCode方法里面,示例:package com.qigang.da; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Registry$Adpative implements com.qigang.da.Registry { public java.lang.String register(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) { if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; String extName = url.getParameter("registry", "zookeeper"); if(extName == null) throw new IllegalStateException("Fail to get extension(com.qigang.da.Registry) name from url(" + url.toString() + ") use keys([registry])"); com.qigang.da.Registry extension = (com.qigang.da.Registry)ExtensionLoader.getExtensionLoader(com.qigang.da.Registry.class).getExtension(extName); return extension.register(arg0, arg1); } public java.lang.String discovery(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) { if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; String extName = url.getParameter("registry", "zookeeper"); if(extName == null) throw new IllegalStateException("Fail to get extension(com.qigang.da.Registry) name from url(" + url.toString() + ") use keys([registry])"); com.qigang.da.Registry extension = (com.qigang.da.Registry)ExtensionLoader.getExtensionLoader(com.qigang.da.Registry.class).getExtension(extName); return extension.discovery(arg0, arg1); } } -
代理类的方法中会判断URL中的参数(如果接口方法有URL参数就会解析这个URL参数值),决定最终调用的是哪个扩展实现类的方法,如果URL中没有指定,则使用接口@Spi(value=”xxx”)指定的默认实现类
-
URL中的参数key默认使用接口名称的英文单词小写,多个单词之间用点隔开,如果@Adaptive(value=”xxx”)指定了参数,那URL种的key就是指定的这个参数值
-
- 具有@Adaptive标签的实现类的使用优先级高于URL中指定参数的方式
对@Adaptive的使用贯穿dubbo的各个组件,例如注册中心组件使用的接口
/**
* RegistryFactory. (SPI, Singleton, ThreadSafe)
*
* @see com.alibaba.dubbo.registry.support.AbstractRegistryFactory
* @author william.liangf
*/
@SPI("dubbo")
public interface RegistryFactory {
/**
* 连接注册中心.
*
* 连接注册中心需处理契约:<br>
* 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。<br>
* 2. 支持URL上的username:password权限认证。<br>
* 3. 支持backup=10.20.153.10备选注册中心集群地址。<br>
* 4. 支持file=registry.cache本地磁盘文件缓存。<br>
* 5. 支持timeout=1000请求超时设置。<br>
* 6. 支持session=60000会话超时或过期设置。<br>
*
* @param url 注册中心地址,不允许为空
* @return 注册中心引用,总不返回空
*/
@Adaptive({"protocol"})
Registry getRegistry(URL url);
}
@Activate
通过ExtensionLoader.getActivateExtension()方法获取到激活的扩展类,也就是根据某种条件(group, key, order)来激活扩展类
2. 服务暴露
xml配置方式暴露服务
dubbo的配置文件使用的是spring的XML扩展方式,在dubbo-x.x.x.jar/META-INF/spring.handlers文件中定义了处理xml文件的DubboNamespaceHandler,里面注册了很多类型的DubboBeanDefinitionParser,将xml配置文件中的不同节点解析成不同的BeanDefinition。各个BeanDefinition被加载进spring容器之后执行spring bean生命周期中的步骤。
各个配置节点的详细作用参考官方配置文档。
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
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());
}
}
其中服务暴露主要是通过ServiceBean来完成的,它实现了InitializingBean,afterPropertiesSet方法会被首先执行。同时它还实现了ApplicationContextAware,setApplicationContext方法会被调用,它还实现了ApplicationListener,onApplicationEvent方法也会被调用。
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
private static final long serialVersionUID = 213195494150089726L;
private static transient ApplicationContext SPRING_CONTEXT;
private final transient Service service;
private transient ApplicationContext applicationContext;
private transient String beanName;
private transient boolean supportedApplicationListener;
public ServiceBean() {
super();
this.service = null;
}
public ServiceBean(Service service) {
super(service);
this.service = service;
}
public static ApplicationContext getSpringContext() {
return SPRING_CONTEXT;
}
public void setApplicationContext(ApplicationContext applicationContext) {
//...
}
public void setBeanName(String name) {
this.beanName = name;
}
public Service getService() {
return service;
}
public void onApplicationEvent(ContextRefreshedEvent event) {
//...
//如果判断是延迟暴露服务,则调用export方法暴露服务
}
private boolean isDelay() {
//...
}
@SuppressWarnings({"unchecked", "deprecation"})
public void afterPropertiesSet() throws Exception {
//...
//主要是解析xml配置中的各项配置bean,最后如果判断为非延迟暴露则直接调用export方法暴露服务
}
public void destroy() throws Exception {
//...
}
}
在afterPropertiesSet和onApplicationEvent中分别调用了ServiceConfig.export()方法来暴露服务,这里有个问题,两个地方都有export方法,哪个会被调用?跟踪调试会发现,如果spring容器可以成功添加ServiceBean为监听器,也就是可以成功通过反射执行容器的addApplicationListener或者addListener方法,那就不会在afterPropertiesSet方法中执行export方法,而是在onApplicationEvent中执行,否则就执行afterPropertiesSet方法中的export。
ServiceConfig.export()
ServiceConfig.doExport()
ServiceConfig.doExportUrls()
ServiceConfig.doExportUrlsFor1Protocol()
//根据配置的参数生成URL对象,例如
//dubbo://172.17.125.18:20880/com.qigang.dubbo.service.DemoService?anyhost=true&application=dubbo_server&dubbo=2.5.3&interface=com.qigang.dubbo.service.DemoService&methods=sayHello&pid=21536&side=provider×tamp=1581222394366
//如果URL中没有参数scope=remote则首先本地暴露服务,也就是说没有在xml的<dubbo:service>节点配置了scope=remote参数
//本地暴露的时候复制原始的URL并进行一个修改,例如改成:
//injvm://127.0.0.1/com.qigang.dubbo.service.DemoService?anyhost=true&application=dubbo_server&dubbo=2.5.3&interface=com.qigang.dubbo.service.DemoService&methods=sayHello&pid=22380&side=provider×tamp=1581223246560
//本地暴露的协议是injvm
ServiceConfig.exportLocal()
//在这里注册到注册中心
RegistryProtocol.export()
//注册之后暴露服务
DubboProtocol.export()
DubboProtocol.openServer()
//*******************************************
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client can export a service which's only for server to invoke
boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
if (isServer) {
ExchangeServer server = serverMap.get(key);
if (server == null) {
serverMap.put(key, createServer(url));
} else {
// server supports reset, use together with override
server.reset(url);
}
}
}
//*******************************************
DubboProtocol.createServer()
HeaderExchanger.connect()
NettyTransporter.connect()
NettyClient构造函数
AbstractClient构造函数
NettyClient.doOpen()
3. 服务消费
服务消费主要是通过ReferenceBean来实现的,这个类也实现了InitializingBean接口
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
private static final long serialVersionUID = 213195494150089726L;
private transient ApplicationContext applicationContext;
public ReferenceBean() {
super();
}
public ReferenceBean(Reference reference) {
super(reference);
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
}
public Object getObject() throws Exception {
return get();
}
public Class<?> getObjectType() {
return getInterfaceClass();
}
@Parameter(excluded = true)
public boolean isSingleton() {
return true;
}
@SuppressWarnings({ "unchecked"})
public void afterPropertiesSet() throws Exception {
//...
}
}
ReferenceBean.afterPropertiesSet()
ReferenceBean.getObject()
ReferenceConfig.get()
ReferenceConfig.init()
ReferenceConfig.createProxy()
JavassistProxyFactory.getInvoker()