JDK SPI和Dubbo SPI

SPI,全称为(Service Provider Interface),是JDK内置的一种服务提供机制。

一、JDK SPI实现

约定规范:

1.配置文件必须在指定文件夹 META-INF/services/ 内。

2.配置文件编码格式必须为:UTF-8。

3.配置文件名必须是 **类的全限定名**。

4.接口如果有多个实现,在配置文件中以 # 作为分隔符。

<code>JDK SPI源码:
// 重载函数, 默认为 当前线程的 ContextClassLoader

public static ServiceLoader load(Class service) {

ClassLoader cl = Thread.currentThread().getContextClassLoader();

return ServiceLoader.load(service, cl);

}

//返回 ServiceLoader 实例

public static ServiceLoader load(Class service, ClassLoader loader) {

return new ServiceLoader<>(service, loader);

}
/<code>

示例:

一个SPI的实现类,比如实现UserService接口为例。

需要SPI扩展接口

io.service.UserService

需要实现类

io.service.impl.UserServiceImpl

加载流程:

以 java.util.ServiceLoader 类的 load 方法作为入口

<code>ServiceLoader<userservice> serviceLoader =  ServiceLoader.load(UserService.class);/<userservice>/<code>


0.在 resource 文件夹下创建子文件夹 META-INF.services,并新增名为: io.service.UserService 的文件,文件内容如下所示:

io.service.impl.UserServiceImpl

1.获取META-INF/services/目录下文件

<code>```
private Iterator<string> parse(Class> service, URL u)
throws ServiceConfigurationError{}
```/<string>/<code>

其中service 表示需要加载的类对象, u 表示 META-INF.services 下文件 在系统中的全路径。


2.解析文件

根据#分割成多个文件,写入List<string>集合/<string>


3.上面集合迭代实现加载(public final class ServiceLoader implements Iterable),然后通过反射,cn为文件全路径名,当前线程的ClassLoader

<code>```
//通过反射,获取目标类
c = Class.forName(cn, false, loader);
```/<code>


4.目标类转换成相应类

<code>```
// 目标类实例转换为响应的类,并将其加载到 providers 列表中
S p = service.cast(c.newInstance());
providers.put(cn, p);
```/<code>

写入将ServiceLoader类的属性provides,底层是LinkedHashMap,保证调用顺序

<code>```
// Cached providers, in instantiation order
private LinkedHashMap<string> providers = new LinkedHashMap<>();
```/<string>/<code>

5.使用 Iterator 接口的实现类LazyIterator,将服务加载的这个动作延迟到使用服务时。这样做的好处是:**只有在真正使用时才加载,避免造成没必要的加载。但未解决,未使用扩展加载的问题**。

<code>// 用于延迟加载接口的实现类
private LazyIterator lookupIterator;/<code>

缺点:

虽然使用延迟加载,但是只能通过遍历全部获取,接口的实现类全部加载并实例化一遍。如果不想用某些实现类,它也被加载并实例化,这就造成了资源浪费。

二、Dubbo SPI扩展机制

Dubbo对JDK SPI进行了扩展,对服务提供者配置文件中的内容进行了改造,由原来的提供者类的全限定名列表改成了KV形式的列表,这也导致了Dubbo中无法直接使用JDK ServiceLoader。

Dubbo默认依次扫描META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/三个classpath目录下的配置文件。配置文件以具体扩展接口全名命名

JDK SPI和Dubbo SPI

dubbo spi

JDK SPI和Dubbo SPI

dubbo spi

流程:

1.dubbo核心实现类ExtensionLoader,通过接口类名和key值获取一个实现类。

2.通过Adaptive实现,生成一个代理类,根据实际调用时,先静态,后动态决定要调用的类。

3.采用装饰器模式进行功能增强,自动包装实现,这种实现的类一般是自动激活的。

JDK SPI和Dubbo SPI

ExtensionLoader总体流程

源码解析:

1首先加载实例:

ExtensionLoader充当插件工厂角色,提供了一个私有的构造器。其入参type为扩展接口类型。Dubbo通过SPI注解定义了可扩展的接口,如Filter、Transporter等。每个类型的扩展对应一个ExtensionLoader。SPI的value参数决定了默认的扩展实现

当扩展类型是ExtensionFactory时,不指定objectFactory,否则初始化ExtensionFactory的ExtensionLoader并获取一个扩展适配器

<code>
@SPI("dubbo")
public interface Protocol {

/**
* 获取缺省端口,当用户没有配置端口时使用。
*
* @return 缺省端口
*/
int getDefaultPort();

/**
* 暴露远程服务:
* 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();

* 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。


* 3. export()传入的Invoker由框架实现并传入,协议不需要关心。

*
* @param invoker 服务的执行体
*/
@Adaptive
Exporter export(Invoker invoker) throws RpcException;

/**
* 引用远程服务:
* 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。

* 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。

* 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。

*
* @param type 服务的类型
* @param url 远程服务的URL地址
*/
@Adaptive
Invoker refer(Class type, URL url) throws RpcException;

/**
* 释放协议:

*/
void destroy();

}


public class DubboProtocol extends AbstractProtocol {

public static final String NAME = "dubbo";
...
...
}

// 示例:
ExtensionLoader<protocol> protocolLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
// 根据Key获取相应的扩展实现类实例
Protocol dubboProtocol = protocolLoader.getExtension(DubboProtocol.NAME);/<protocol>
/<code>

其次,getExtension是实现整个扩展加载器ExtensionLoader中最核心的方法。分为4步,

1.1 读取配置文件,并缓存;

<code>     // 1. 先从缓存中取相应的扩展实现类实例
Holder<object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<object>());
holder = cachedInstances.get(name);
}

Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建相应的扩展实现类实例,并缓存
instance = createExtension(name);
holder.set(instance);
}
}
}/<object>/<object>/<code>

1.2 根据传入的名称初始化扩展类;

<code>     // 2. 根据name获取相应扩展类的类实例
Class> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}

try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
\t //注入依赖,即IOC
injectExtension(instance);
Set<class>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class> wrapperClass : wrapperClasses) {
\t //通过反射创建Wrapper实例,向Wrapper实例注入依赖,最后赋值给instance,自动包装实现类似aop功能
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
\t\t//返回扩展类示例
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}/<class>/<code>

1.3 尝试查找符合条件的包装类,符合条件的注入扩展类(反射实现的,查询set开头的方法),并注入类的实例;

<code>private T injectExtension(T instance) {
try {
if (this.objectFactory != null) {
Method[] var2 = instance.getClass().getMethods();
\tint var3 = var2.length;
\tfor(int var4 = 0; var4 < var3; ++var4) {

Method method = var2[var4];
if (this.isSetter(method) && method.getAnnotation(DisableInject.class) == null) {
Class> pt = method.getParameterTypes()[0];
\tif (!ReflectUtils.isPrimitives(pt)) {
try {
String property = this.getSetterProperty(method);
\tObject object = this.objectFactory.getExtension(pt, property);
\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (object != null) {
// 注入类的实例
method.invoke(instance, object);
}
} catch (Exception var9) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + this.type.getName() + ": "
+ var9.getMessage(), var9);
}
}
}
}
}
} catch (Exception var10) {
logger.error(var10.getMessage(), var10); }

return instance;
}/<code>

1.4 返回扩展类实例。

2.自适应扩展器

dubbo提供了两种方式来生成扩展适配器:静态适配器扩展和动态适配器扩展。

静态适配器扩展,就是提前通过编码的形式确定扩展的具体实现,且该实现类由Adaptive注解标注,如:AdaptiveCompiler。在加载配置文件的loadFile方法中,已经描述过处理该类型扩展的逻辑,具体可参考loadFile()方法源码。

动态适配器扩展,

即通过动态代理生成扩展类的动态代理类,在dubbo中是通过javassist技术生成的。与传统的jdk动态代理、cglib不同,javassist提供封装后的API对字节码进行间接操作,简单易用,不关心具体字节码,灵活性更高,且处理效率也较高,是dubbo默认的编译器。

2.1 首先,从ExtensionLoader构造器中会调用getAdaptiveExtension()方法触发为当前扩展类型生成适配器:

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

public T getAdaptiveExtension() {
// 1. 首先,检查是否存在当前扩展类静态适配器
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if(createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 2. 创建当前扩展类动态适配器
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
}
else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);

}
}

return (T) instance;
}/<code>

2.2 创建动态类扩展适配器

<code> private T createAdaptiveExtension() {
try {
// IOC属性注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}/<code>

2.3 根据adaptive注解的value数组值,及SPI注解定义的默认扩展名,确定适配逻辑,即扩展获取的优先级,以下代码为例其获取优先级是server,transporter,netty

<code>package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty")); // 处理顺序
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty")); // 处理顺序
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}/<code>

2.4 拿到扩展名后,再从ExtensionLoader获取到扩展实例,调用具体的bind方法。


源码生成后,ExtensionLoader再调用默认的JavassitCompiler进行编译和类加载。


3.1 dubbo中存在一种对于扩展的封装类,其功能是将各扩展实例串联起来,形成扩展链,比如过滤器链,监听链。当调用ExtensionLoader的getExtension方法时,会做拦截处理,如果存在封装器,则返回封装器实现,而将真实实现通过构造方法注入到封装器中

<code>        Set<class>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}/<class>/<code>


分享到:


相關文章: