Motan的两个注解
- Spi,在Motan中如果要使用SPI机制,则暴露出来的接口要使用@Spi注解标注,并且可以指定该接口的的实现者在创建对象时是用单例还是多例创建。
- SpiMeta ,这个注解是加载实现类上的,用来标注该实现类的SPI名称,后续可以通过该名称来获取一个服务。(一个接口会有很多实现类,可以标注每个实现类自己的名称)
SPI的核心处理类是motan-core中的ExtensionLoader
这里跟java的SPI相似的地方是,都要在实现类所在的包中增加META-INF/services/目录,并在该目录下增加SPI的描述。
- 主入口:
// 通过getExtensionLoader获取一个类型的Loader,
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
checkInterfaceType(type);
// 先看是否已经存在该类型的Loader
ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type);
if (loader == null) {
// 不存在,这里会创建一个该类型Loader的实例,并保存在extensionLoaders中
loader = initExtensionLoader(type);
}
return loader;
}
- 获取SPI的提供者(获取接口实现类的实例)
public T getExtension(String name) {
// 这里会去META-INF/services/目录下查找当前loader要加载类型的所有SPI提供者的描述文件(第一次执行,且执行一次)
checkInit();
if (name == null) {
return null;
}
try {
Spi spi = type.getAnnotation(Spi.class);
// 是单例,这里会创建该名称所对应类的示例并缓存,主要是多了缓存这一步
if (spi.scope() == Scope.SINGLETON) {
return getSingletonInstance(name);
} else {
Class<T> clz = extensionClasses.get(name);
if (clz == null) {
return null;
}
// 非单例,每次都创建新对象
return clz.newInstance();
}
} catch (Exception e) {
failThrows(type, "Error when getExtension " + name, e);
}
return null;
}
- getSingletonInstance的处理
private T getSingletonInstance(String name) throws InstantiationException, IllegalAccessException {
// 先从缓存找
T obj = singletonInstances.get(name);
// 找到返回
if (obj != null) {
return obj;
}
// 没有,则找到改名称所对应的类
Class<T> clz = extensionClasses.get(name);
if (clz == null) {
return null;
}
synchronized (singletonInstances) {
obj = singletonInstances.get(name);
if (obj != null) {
return obj;
}
// 创建示例,并缓存
obj = clz.newInstance();
singletonInstances.put(name, obj);
}
// 返回创建的示例
return obj;
}
java的SPI不需要注解
- 核心包(jar)中提供一个接口
- 在其他包(jar)中写实现类,在jar的META-INF/services/目录下增加SPI配置文件
- 通过ServiceLoader<T> s = ServiceLoader.load(T.class);获取接口的实际实现类
java的SPI只能通过类型获取实现者,最后要根据类型来确定使用哪个实现类来处理业务。
Motan通过SpiMeta注解增加类实现类的名称,所以可以根据名称来获取,能更好的解耦。