1. SPI应用场景之 DUBBO SPI

一、DUBBO SPI使用场景

动态根据配置加载对应实现类,SPI扩展的类有非常多:
1. SPI应用场景之 DUBBO SPI_第1张图片

二、dubbo spi 扩展点

①dubbo SPI基础:

根据META-INF文件下的key获取类名,再获取类的实例
对应方法为,getExtension(),举例:

 		LoadBalance loadBalance = 
            ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("random");
        //获取到RadomLoadBalance类实例
        System.out.println(loadBalance);

②DUBBO SPI增强,自适应扩展机制:

对应方法为,getAdaptiveExtension(),举例:

	//此处获取到的并不是具体的协议实现类,而是获取到一个自适应的Adaptive类
        Protocol protocol = 
                ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
        //通过Adaptive类调用远程服务,可根据url的协议自适应到:具体协议实现类,完成远程协议调用
        protocol.refer(type, url);
				

什么叫自适应,引用官网的解释

有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。

我们再举个例子解释下,例如:dubbo提供了很多协议:dubbo、rmi、http、webservice等。但实际我们用的时候,可能只会选择一种协议,假设我们选择使用rmi协议,则dubbo会在运行的时候才去加载RMI的协议实现类(并在加载一次后缓存起来),其他http、webservice等协议实现类没有用到就不会加载。这就是自适应加载。dubbo里面的Adaptive类就是做这个加载动作的。

Adaptive类有2种加载方式:

① 静态

静态Adaptive类,即Adaptive类在项目中是存在的,不需要去动态生成的
当通过getAdaptiveExtension()获取自适应扩展点时,将去查找META-INF下所有类中,有@Adaptive注解的类。

举例:
Compiler的Adaptive类是:AdaptiveCompiler

META-INF/dubbo/internal/com.alibaba.dubbo.common.compiler.Compiler文件如下:

adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

当执行以下代码时,会直接返回@Adaptive标注的AdaptiveCompiler类的实例:

Compiler compiler = 
                ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();

1. SPI应用场景之 DUBBO SPI_第2张图片

② 动态

动态Adaptive类是在调用的时候,字节码是在运行的时候实时生成的,再通过动态反射创建类。

举例:获取Protocol接口的Adaptive类

首先在META-INF下的,com.alibaba.dubbo.rpc.Protocol中,所有的类都没有@Adaptive注解,这个时候会通过代码去生成Adaptive的字节码

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
....... 等等

生成Adaptive类字节码的方法详细可参考:ExtensionLoader.createAdaptiveExtensionClassCode()

1. SPI应用场景之 DUBBO SPI_第3张图片

可以看到生成的类名为 接口名称$Adapative,并且会重写@Adaptive注解的方法
在这个Adaptive类里面,我们可以大胆的猜测,实际就是根据一个关键字获取对应的实现类。那这个关键字从哪里取呢,我们知道dubbo就是通过URL调用的,新增了timeout参数、调用的协议都会在URL中以参数形式存在的… 没错,就是在URL中取…

我们来看下生成之后的代码:
1. SPI应用场景之 DUBBO SPI_第4张图片

我们将代码拷贝出来,格式化下:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
	public void destroy() {
		throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}

	public int getDefaultPort() {
		throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}

	public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1)
			throws com.alibaba.dubbo.rpc.RpcException {
		if (arg1 == null)
			throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg1;
		String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
		if (extName == null)
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}

	public com.alibaba.dubbo.rpc.Exporter export(
			com.alibaba.dubbo.rpc.Invoker arg0)
			throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null)
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null)
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
		com.alibaba.dubbo.common.URL url = arg0.getUrl();
		String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
		if (extName == null)
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.export(arg0);
	}
}

Protocol类有两个@Adaptive方法,export()-》服务暴露; refer()-》服务调用;
通过这两个方法也能很明显的看出就是从url中获取到协议,然后根据协议名称,获取具体的实现类
1. SPI应用场景之 DUBBO SPI_第5张图片

最后,再用一张思维导图总结下:
1. SPI应用场景之 DUBBO SPI_第6张图片

你可能感兴趣的:(dubbo源代码)