相关博客:

​​Dubbo的SPI机制(一)(Java的SPI)​​

​​Dubbo 的 SPI 机制(三)(Extension 扩展点补充)​​

 

目录

​​Dubbo的SPI规范​​

​​Dubbo的SPI示例​​

​​Extension扩展点分析​​


在Dubbo的官方文档中,有一部分是专门介绍SPI扩展实现:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务

Dubbo的SPI规范

大部分的思想都是和SPI是一样,只是下面两个地方有差异。

1. 需要在resources目录下配置META-INF/dubbo或者META-INF/dubbo/internal或者META-INF/services,并基于SPI接口去创建一个文件;

2. 文件名称和接口全类名保持一致,文件内容和Java的SPI有差异,内容是KEY对应Value;

Dubbo的SPI示例

下面以协议扩展为例:

结合官方文档,实现扩展接口:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_加载_02

(我这里使用的Dubbo版本为2.5.3,与官网最新版的文档有所不一样)

在dubboclient中实现Exporter接口:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_加载_03

根据规范,创建resources/MRTA-INF/dubbo文件夹,并创建以Exporter全类名为文件名的文件:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_04

文件内容为:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_05

运行测试代码:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_加载_06

抛出了一个异常:

Exception in thread "main" java.lang.IllegalArgumentException: Extension type(interface com.alibaba.dubbo.rpc.Exporter) is not extension, because WITHOUT @SPI Annotation!
2018-11-24 16:25:25,920 INFO [com.alibaba.dubbo.common.logger.LoggerFactory] - using logger: com.alibaba.dubbo.common.logger.log4j.Log4jLoggerAdapter
at com.alibaba.dubbo.common.extension.ExtensionLoader.getExtensionLoader(ExtensionLoader.java:115)
at dongguabai.dubbo.App.main(App.java:21)

说是没有使用@SPI注解,那么先来看看这个注解:

/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.extension;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 扩展点接口的标识。
* <p />
* 扩展点声明配置文件,格式修改。<br />
* 以Protocol示例,配置文件META-INF/dubbo/com.xxx.Protocol内容:<br />
* 由<br/>
* <pre><code>com.foo.XxxProtocol
com.foo.YyyProtocol</code></pre><br/>
* 改成使用KV格式<br/>
* <pre><code>xxx=com.foo.XxxProtocol
yyy=com.foo.YyyProtocol
* </code></pre>
* <br/>
* 原因:<br/>
* 当扩展点的static字段或方法签名上引用了三方库,
* 如果三方库不存在,会导致类初始化失败,
* Extension标识Dubbo就拿不到了,异常信息就和配置对应不起来。
* <br/>
* 比如:
* Extension("mina")加载失败,
* 当用户配置使用mina时,就会报找不到扩展点,
* 而不是报加载扩展点失败,以及失败原因。
*
* @author william.liangf
* @author ding.lid
* @export
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {

/**
* 缺省扩展点名。
*/
String value() default "";

}

由于我这里实现的是Exporter接口,这个接口是没有添加@SPI注解的:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_07

接下来改为实现Protocol接口:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_08

同样的,根据规范,在resources/MRTA-INF/dubbo文件夹下创建以Protocol全类名为文件名的文件:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_09

文件内容为:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_10

再运行测试代码:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_11

输出结果,可以看到调用成功:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_12

Extension扩展点分析

在dubbo-common下有AdaptiveExtensionFactory和SpiExtensionFactory:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_13

还有三个注解:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_14

在dubbo-config-spring下有SpringExtensionFactory:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_15

这三个类和三个注解是Dubbo Extension的主要结构:

  • AdaptiveExtensionFactory
  • SpiExtensionFactory
  • SpringExtensionFactory
  • @SPI
  • @Adaptive
  • @Activate

之前实现的Protocol接口,已经被标注了@SPI注解,代表是一个扩展点,默认key是dubbo:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_16

在dubbo-rpc-dubbo中也能够找到相应的实现:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_17

内容为:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_18

所以之前的测试代码中,还有这样一个方法:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_19

这个就是获取默认扩展。可以简单测试一下:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_20

运行结果: 

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_21

的确默认端口即为20880:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_22

在Protocol接口的export()和refer()方法上使用了@Adaptive注解:

/**
* 暴露远程服务:<br>
* 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
* 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
* 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
*
* @param <T> 服务的类型
* @param invoker 服务的执行体
* @return exporter 暴露服务的引用,用于取消暴露
* @throws RpcException 当暴露服务出错时抛出,比如端口已占用
*/
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

/**
* 引用远程服务:<br>
* 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
* 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>
* 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
*
* @param <T> 服务的类型
* @param type 服务的类型
* @param url 远程服务的URL地址
* @return invoker 服务的本地代理
* @throws RpcException 当连接服务提供方失败时抛出
*/
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

在ServiceConfig中是这么使用的:

public class ServiceConfig<T> extends AbstractServiceConfig {

private static final long serialVersionUID = 3033787999037024738L;

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
...

 先看看getExtensionLoader()方法,这个方法就是根据一个Class去获取一个扩展的Loader,代码也很简单:

@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if(!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}

ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
//没有就new一个
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
//有就直接获取
return loader;
}

就是根据类型去从EXTENSION_LOADERS中获取Loader,这个EXTENSION_LOADERS就是一个 ConcurrentHashMap:

private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

ExtensionLoader的私有的构造方法:

private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

再看getAdaptiveExtension()方法,这个方法的作用是获取一个自适应扩展点

总体来看这个方法:

会先从一个cacheAdaotiveInstance中获取一个示例,如果不为null,直接返回,为null就生成instance后再set给cacheAdaotiveInstance,中间还使用的DCL机制:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_23

首先会从cachedAdaptiveInstance中获取一个instance:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_24

看看这个cachedAdaptiveInstance:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_加载_25

是一个Holder实例。再看看Holder,主要作用是用来存值:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_加载_26

再接着看看getAdaptiveExtension()方法:

经过一系列的判断后,如果instance为null,则调用createAdaptiveExtension()方法:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_27

看看createAdaptiveExtension()方法:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_28

先看getAdaptiveExtensionClass()方法:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_29

先看createAdaptiveExtensionClass()方法:

//创建一个适配器扩展点,基于动态字节码的方式编译
private Class<?> createAdaptiveExtensionClass() {
//生成的字节码代码
String code = createAdaptiveExtensionClassCode();
//获得类加载器
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//动态编译
return compiler.compile(code, classLoader);
}

运行之前的程序:debug看看:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_加载_30

code中的内容,即Protocol$Adpative动态生成的内容为:

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.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
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);
}
//引用服务
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
//根据url参数去做判断
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);
}
}

这里需要结合Dubbo服务发布的代码:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_31

可以看到初始化的时候会去加载(final)这个适配器的扩展点,也就是上面介绍的Protocol$Adpative,这样一个动态的类文件。

实际上是调用的Protocol接口的export()方法,而这个方法肯定是有很多是实现的:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_远程服务_32

在看看getExtensionClasses()方法,看名字就可以猜出来,主要是用来加载扩展点的实现类:

//加载扩展点的实现类
private Map<String, Class<?>> getExtensionClasses() {
//以Protocol为例,会将Protocol所有的扩展实现放到Map中
Map<String, Class<?>> classes = cachedClasses.get();
//DCL
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//如果Map为空,加载Map
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}

看下loadExtensionClasses()方法:

// 此方法已经getExtensionClasses方法同步过。
private Map<String, Class<?>> loadExtensionClasses() {
//这个type就是比如传过来的Protocol.class
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
//获取@SPI注解中的信息
String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) { //不为空
String[] names = NAME_SEPARATOR.split(value);
if(names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if(names.length == 1) cachedDefaultName = names[0];
}
}

Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//接下来加载Dubbo SPI中的扩展实现
// private static final String SERVICES_DIRECTORY = "META-INF/services/";

// private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";

// private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}

这个loadFile()方法很长,但是也能够大致推测出会遍历目录下的文件,加载相应的Class:

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
String line = null;
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {//文件采用name=value方式,通过i进行分割
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
Class<?> clazz = Class.forName(line, true, classLoader);
//加载对应的实现类,并且判断实现类必须是当前的加载的扩展点的实现
if (! type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}

//判断是否有自定义适配类,如果有,则在前面讲过的获取适配类的时候,直接返回当前的自定义适配类,不需要再动态创建
// 还记得在前面讲过的getAdaptiveExtensionClass中有一个判断吗?是用来判断cachedAdaptiveClass是不是为空的。如果不为空,表示存在自定义扩展点。也就不会去动态生成字节码了。这个地方可以得到一个简单的结论;
// @Adaptive如果是加在类上, 表示当前类是一个自定义的自适应扩展点
//如果是加在方法级别上,表示需要动态创建一个自适应扩展点,也就是Protocol$Adaptive
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
try {
//如果没有Adaptive注解,则判断当前类是否带有参数是type类型的构造函数,如果有,则认为是
//wrapper类。这个wrapper实际上就是对扩展类进行装饰.
//可以在dubbo-rpc-api/internal下找到Protocol文件,发现Protocol配置了3个装饰
//分别是,filter/listener/mock. 所以Protocol这个实例来说,会增加对应的装饰器
clazz.getConstructor(type);//
//得到带有public DubboProtocol(Protocol protocol)的扩展点。进行包装
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);//包装类 ProtocolFilterWrapper(ProtocolListenerWrapper(Protocol))
} catch (NoSuchMethodException e) {
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
} else {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (! cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + url + ") in " + url, t);
}
} // end of while urls
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}

关于@Adaptive注解:

  • @Adaptive如果是加在类上, 表示当前类是一个自定义的自适应扩展点
  • 如果是加在方法级别上,表示需要动态创建一个自适应扩展点,也就是Protocol$Adaptive
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.extension;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.alibaba.dubbo.common.URL;

/**
* 在{@link ExtensionLoader}生成Extension的Adaptive Instance时,为{@link ExtensionLoader}提供信息。
*
* @author ding.lid
* @export
*
* @see ExtensionLoader
* @see URL
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {

/**
* 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。
* <p>
* 如果{@link URL}这些Key都没有Value,使用 用 缺省的扩展(在接口的{@link SPI}中设定的值)。<br>
* 比如,<code>String[] {"key1", "key2"}</code>,表示
* <ol>
* <li>先在URL上找key1的Value作为要Adapt成的Extension名;
* <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。
* <li>key2没有Value,使用缺省的扩展。
* <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。
* </ol>
* <p>
* 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>
* 即对于Extension接口{@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}的缺省值为<code>String[] {"yyy.invoker.wrapper"}</code>
*
* @see SPI#value()
*/
String[] value() default {};

}

总的来说,getAdaptiveExtensionClass()方法获取了Protocol$Adpative。主要做了两个事情:

  1. getExtensionClasses() 加载所有路径下的扩展点
  2. createAdaptiveExtensionClass() 动态创建一个扩展点

cachedAdaptiveClass这里有个判断,用来判断当前Protocol这个扩展点是否存在一个自定义的适配器,如果有,则直接返回自定义适配器,否则,就动态创建,这个值是在getExtensionClasses中赋值的。

createAdaptiveExtensionClass()动态生成适配器代码,以及动态编译:

  1. createAdaptiveExtensionClassCode,  动态创建一个字节码文件。返回code这个字符串
  2. 通过compiler.compile进行编译(默认情况下使用的是javassist)
  3. 通过ClassLoader加载到jvm中

发现很大一部分是与Adaptive相关,那这个自适应到底有什么用呢?

自适应从字面意思理解是在运行的时候可以根据当前的状态去执行或者生成符合当前场景的东东。

接下来重点看看Protocol$Adpative的refer()和export()方法,一个是引用一个是发布:

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.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
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);
}
//引用服务
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
//根据url参数去做判断,如果是null,则默认使用dubbo协议
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)
//根据名称获取相应名称的扩展带点,比如hessian
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}

Protocol$Adaptive的主要功能

     1.从url或扩展接口获取扩展接口实现类的名称;

     2.根据名称,获取实现类ExtensionLoader.getExtensionLoader(扩展接口类).getExtension(扩展接口实现类名称),然后调用实现类的方法。

需要明白一点dubbo的内部传参基本上都是基于Url来实现的,也就是说Dubbo是基于URL驱动的技术。

所以,适配器类的目的是在运行期获取扩展的真正实现来调用,解耦接口和实现,这样的话要不我们自己实现适配器类,要不dubbo帮我们生成,而这些都是通过Adpative来实现。

到目前为止,我们的AdaptiveExtension的主线走完了,可以简单整理一下他们的调用关系如下:

Dubbo的SPI机制(二)(Dubbo优化后的SPI实现)_java_33

也就是说,之前的这段代码:

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class). getAdaptiveExtension();

 最终的protocol等于Protocol$Adaptive。