motan扩展机制

motan第二篇,本来想写motan的rpc调用过程的,但项目中的需求需要对motan进行扩展,所以就先记录下

引导

在写一个框架或者在项目中提供一些底层服务时,都会有这种情况,会有一些默认的实现,但你知道这些默认实现只是很满足很基本的自身需求,开发人员可能会扩展,想自定义一个实现

处理方式

  1. 提供一个设置实现类的setter,开发者在初始化时调用一下
  2. 提供配置入口,给个key,配置上自定义类名
  3. 类似slf4j一样,提供桥接类

SPI

motan使用了spi的方式

SPI(Service Provider Interface),服务提供接口;也是一种服务发现机制

1
2
3
4
5
6
7
8
9
系统里抽象的各个模块,往往有很多不同的实现方案,
比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,
我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码
。一旦代码里涉及具体的实现类,就违反了可拔插的原则,
如果需要替换一种实现,就需要修改代码。

为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。
有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

在JDK6之前,你可能会自己定义一种服务提供的约定,在JDK6之后,java也提供了标准约定

1
2
扩展者在jar包的META-INF/services/目录下放置与接口同名的文本文件 
内容为接口实现类名,多个实现类名用换行符分隔

java.util.ServiceLoader类来实现从配置文件中加载子类或者接口的实现类

SPI与API的区别

1
2
3
4
5
6
7
8
9
10
11
12
What is the difference between Service Provider Interface (SPI) and Application Programming Interface (API)?
More specifically, for Java libraries, what makes them an API and/or SPI?
the API is the description of classes/interfaces/methods/...
that you call and use to achieve a goal
the SPI is the description of classes/interfaces/methods/...
that you extend and implement to achieve a goal
Put differently, the API tells you what a specific class/method does for you and the SPI tells you what you must do to conform.
Sometimes SPI and API overlap.
For example in JDBC the Driver class is part of the SPI:
If you simply want to use JDBC, you don't need to use it directly, but everyone who implements a JDBC driver must implement that class.
The Connection interface on the other hand is both SPI and API:
You use it routinely when you use a JDBC driver and it needs to be implemented by the developer of the JDBC driver。

JDK spi

使用jdk自带的ServiceLoader写一个示例:

一个接口

1
2
3
4
public interface Spi {

public void provide();
}

一个实现类

1
2
3
4
5
6
7
public class DefaultSpi implements Spi{

@Override
public void provide() {
System.out.println("默认spi实现");
}
}

入口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 一个spi的demo
*
*/
public class SpiDemoMain
{
public static void main( String[] args )
{
ServiceLoader<Spi> spiServiceLoader = ServiceLoader.load(Spi.class);

Iterator<Spi> spiIterator = spiServiceLoader.iterator();

while ( spiIterator.hasNext()) {
spiIterator.next().provide();
}
}
}

在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
29
private 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差不多,但做了一些加强

先看官方文档

  1. 实现SPI扩展点接口
  1. 实现类增加注解
    @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 给类一个别名,方便配置时使用

公众号:码农戏码
欢迎关注微信公众号『码农戏码』