根据META-INF文件下的key获取类名,再获取类的实例
对应方法为,getExtension(),举例:
LoadBalance loadBalance =
ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("random");
//获取到RadomLoadBalance类实例
System.out.println(loadBalance);
对应方法为,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类,即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();
动态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()
可以看到生成的类名为 接口名称$Adapative,并且会重写@Adaptive注解的方法
在这个Adaptive类里面,我们可以大胆的猜测,实际就是根据一个关键字获取对应的实现类。那这个关键字从哪里取呢,我们知道dubbo就是通过URL调用的,新增了timeout参数、调用的协议都会在URL中以参数形式存在的… 没错,就是在URL中取…
我们将代码拷贝出来,格式化下:
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中获取到协议,然后根据协议名称,获取具体的实现类