motan第二篇,本来想写motan的rpc调用过程的,但项目中的需求需要对motan进行扩展,所以就先记录下
引导
在写一个框架或者在项目中提供一些底层服务时,都会有这种情况,会有一些默认的实现,但你知道这些默认实现只是很满足很基本的自身需求,开发人员可能会扩展,想自定义一个实现
处理方式
- 提供一个设置实现类的setter,开发者在初始化时调用一下
- 提供配置入口,给个key,配置上自定义类名
- 类似slf4j一样,提供桥接类
SPI
motan使用了spi的方式
SPI(Service Provider Interface),服务提供接口;也是一种服务发现机制
1 | 系统里抽象的各个模块,往往有很多不同的实现方案, |
在JDK6之前,你可能会自己定义一种服务提供的约定,在JDK6之后,java也提供了标准约定1
2扩展者在jar包的META-INF/services/目录下放置与接口同名的文本文件
内容为接口实现类名,多个实现类名用换行符分隔
java.util.ServiceLoader类来实现从配置文件中加载子类或者接口的实现类
SPI与API的区别
1 | What is the difference between Service Provider Interface (SPI) and Application Programming Interface (API)? |
JDK spi
使用jdk自带的ServiceLoader写一个示例:
一个接口
1 | public interface Spi { |
一个实现类1
2
3
4
5
6
7public class DefaultSpi implements Spi{
@Override
public void provide() {
System.out.println("默认spi实现");
}
}
入口类
1 | /** |
在META-INF文件夹里面新建个services文件夹,在services文件夹里面新建一个
com.jjk.spi.Spi文件
完整的代码可从https://github.com/zhuxingsheng/spidemo下载
ServiceLoader源码解析
原理很简单,一个类实现这个SPI机制
它的本质,也就是从某个地方加载服务实现类,文件名是服务接口名
定义存放文件的地方1
private static final String PREFIX = "META-INF/services/";
类内部使用了延迟加载LazyIterator,在使用到了实现类时,才去实例化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
// 调用next方法时,才实例化
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
motan spi
motan的spi,跟java spi差不多,但做了一些加强
先看官方文档
- 实现SPI扩展点接口
- 实现类增加注解
@Spi(scope = Scope.SINGLETON) //扩展加载形式,单例或多例
@SpiMeta(name = “motan”) //name表示扩展点的名称,根据name加载对应扩展
@Activation(sequence = 100) //同类型扩展生效顺序,部分扩展点支持。非必填
增加SPI实现声明 ${classpath}/MATA-INF/services/${SPI interface fullname}文件中添加对应SPI接口实现类全名。 可参照motan-core模块/MATA-INF/services/下的配置
主要类就是com.weibo.api.motan.core.extension.ExtensionLoader
代码在https://github.com/zhuxingsheng/motan/blob/master/motan-core/src/main/java/com/weibo/api/motan/core/extension/ExtensionLoader.java#L100-99
其实代码很简单,不需要额外的解读,都能看明白
还有几个注解类
@Spi 指定类生命周期
@SpiMeta 给类一个别名,方便配置时使用