dubbo是高度可扩展,用户几乎可以在任何功能点进行自定义实现。而这些高扩展性,得益于ExtensionLoader的实现。
下面我们一起分析下ExtensionLoader。
什么是ExtensionLoader
先来看下ExtensionLoader类的注释
/**
* Load dubbo extensions
* <ul>
* <li>auto inject dependency extension </li>
* <li>auto wrap extension in wrapper </li>
* <li>default extension is an adaptive instance</li>
* </ul>
*/
public class ExtensionLoader<T> {
}
简单翻译下:
加载dubbo扩展
- 自动注入依赖扩展
- 自动封装扩展
- 默认扩展是一个可适应的实例
光看注释,其实我们还是没法知道ExtensionLoader到底是什么?还是得通过例子来理解。
一个简单例子
public static void main(String[] args) throws IOException {
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol protocol = extensionLoader.getExtension("dubbo");
System.out.println(protocol);
}
输出:
com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper@3fee9989
怎么输出了ProtocolFilterWrapper,而不是DubboProtocol?
ExtensionLoader.getExtensionLoader(Protocol.class)
首先看下ExtensionLoader.getExtensionLoader(Protocol.class) 做了什么。
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
将Protocol.class作为参数创建了ExtensionLoader
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
ExtensionLoader有两个成员变量,type是我们传的Protocol.class,objectFactory是用来给生成的扩展对象属性赋值。
创建好ExtensionLoader后,放入EXTENSION_LOADERS中,再次获取可以从缓存中获取。
extensionLoader.getExtension(“dubbo”)
接下来,再看下extensionLoader.getExtension(“dubbo”),代码执行过程如下:
简单总结下上面的流程:
- 根据loadDirectory方法从指定目录加载type全类名的文件;
- 通过loadResource方法循环解析文件的每一行,以等号拆分,等号左边为name,右边为class;
- 通过loadClass方法把解析出的class根据不同类型放入不同的缓存mapp中,比如wrapper类放入cachedWrapperClasses缓存。
- 创建实例,通过injectExtension方法给实例属性赋值。
注解@Adaptive
Adaptive有什么用?
我们来看一个例子
public static void main(String[] args) throws IOException {
ExtensionLoader<Serialization> extensionLoader = ExtensionLoader.getExtensionLoader(Serialization.class);
Serialization serialization = extensionLoader.getAdaptiveExtension();
System.out.println(serialization);
}
输出:
com.alibaba.dubbo.common.serialize.Serialization$Adaptive@276438c9
生成了一个新的类Serialization$Adaptive,我们先来看下Serialization的代码
@SPI("hessian2")
public interface Serialization {
/**
* get content type id
*
* @return content type id
*/
byte getContentTypeId();
/**
* get content type
*
* @return content type
*/
String getContentType();
/**
* create serializer
*
* @param url
* @param output
* @return serializer
* @throws IOException
*/
@Adaptive
ObjectOutput serialize(URL url, OutputStream output) throws IOException;
/**
* create deserializer
*
* @param url
* @param input
* @return deserializer
* @throws IOException
*/
@Adaptive
ObjectInput deserialize(URL url, InputStream input) throws IOException;
}
再对比下生成的新类Serialization$Adaptive,
public class Serialization$Adaptive implements com.alibaba.dubbo.common.serialize.Serialization {
public byte getContentTypeId() {
throw new UnsupportedOperationException("method public abstract byte com.alibaba.dubbo.common.serialize.Serialization.getContentTypeId() of interface com.alibaba.dubbo.common.serialize.Serialization is not adaptive method!");
}
public com.alibaba.dubbo.common.serialize.ObjectOutput serialize(com.alibaba.dubbo.common.URL arg0, java.io.OutputStream arg1) throws java.io.IOException {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("serialization", "hessian2");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.serialize.Serialization) name from url(" + url.toString() + ") use keys([serialization])");
com.alibaba.dubbo.common.serialize.Serialization extension = (com.alibaba.dubbo.common.serialize.Serialization) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.serialize.Serialization.class).getExtension(extName);
return extension.serialize(arg0, arg1);
}
public com.alibaba.dubbo.common.serialize.ObjectInput deserialize(com.alibaba.dubbo.common.URL arg0, java.io.InputStream arg1) throws java.io.IOException {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("serialization", "hessian2");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.serialize.Serialization) name from url(" + url.toString() + ") use keys([serialization])");
com.alibaba.dubbo.common.serialize.Serialization extension = (com.alibaba.dubbo.common.serialize.Serialization) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.serialize.Serialization.class).getExtension(extName);
return extension.deserialize(arg0, arg1);
}
public java.lang.String getContentType() {
throw new UnsupportedOperationException("method public abstract java.lang.String com.alibaba.dubbo.common.serialize.Serialization.getContentType() of interface com.alibaba.dubbo.common.serialize.Serialization is not adaptive method!");
}
}
从中可以发现
- 没加@Adaptive的方法,直接抛出异常UnsupportedOperationException;
- 加了注解的方法主要逻辑
- 根据url获取参数中的扩展名extName
- 再根据扩展名extName获取对应的扩展类
具体代码生成逻辑,有兴趣的可以看下源码实现
com.alibaba.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClassCode
注解@Activate
最后看下Activate
public static void main(String[] args) throws IOException {
URL url = URL.valueOf("test://localhost/test");
List<Filter> list = ExtensionLoader.getExtensionLoader(Filter.class)
.getActivateExtension(url, new String[]{}, Constants.CONSUMER);
System.out.println(list);
}
输出:
[com.alibaba.dubbo.rpc.filter.ConsumerContextFilter@525b461a, com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter@58c1c010, com.alibaba.dubbo.monitor.support.MonitorFilter@b7f23d9]
ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(URL url, String[] values, String group),会把Filter.class实现类带@Activate注解且group为Constants.CONSUMER的获取到。
比如
@Activate(group = Constants.CONSUMER, order = -10000)
public class ConsumerContextFilter implements Filter {
}
Dubbo之ExtensionLoader的分享就到这里,有问题欢迎指出。