SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以在不改动接口源代码的情况下,通过 SPI 机制为我们的程序提供拓展功能。
(2) 编写示例新建maven工程后,先定义一个接口
public interface UploadCDN {
void upload(String url);
}
接下来定义两个实现类
public class QiyiCDN implements UploadCDN {
@Override
public void upload(String url) {
System.out.println("upload to Qiyi");
}
}
public class ChinaNetCDN implements UploadCDN {
@Override
public void upload(String url) {
System.out.println("upload to ChinaNetCDN");
}
}
在resource/meta-INF/services下创建一个文本文件,文件名为接口的全限定名:com.lzq.spidemo.service.UploadCDN,并写入以下内容
com.lzq.spidemo.service.Impl.QiyiCDN com.lzq.spidemo.service.Impl.ChinaNetCDN
开始编写测试代码
public class Demo {
public static void main(String[] args) {
//注意:"meta-INF/services/"这个目录是在ServiceLoader写死的
ServiceLoader uploadCDN = ServiceLoader.load(UploadCDN.class);
for (UploadCDN u : uploadCDN) {
u.upload("filePath");
}
}
}
可以看到,所有的实现类都会创建新实例并调用对应方法。
(3) ServiceLoader源码
load函数会将当前线程的类加载器和接口的类对象传入ServiceLoader对象
public staticServiceLoaderload(Classservice) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
创建ServiceLoader对象后,会初始化一个内部的迭代器对象LazyIterator
private class LazyIterator implements Iterator2.Dubbo的SPI机制 (1) 简介{ Classservice; ClassLoader loader; Enumerationconfigs = null; Iterator pending = null; String nextName = null; private LazyIterator(Class service, ClassLoader loader) { this.service = service; this.loader = loader; } private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { //查找到对应接口的文本文件,然后解析获取到其中记录的所有实现类 String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; } }
dubbo的spi在jdk的spi基础上做了扩展,可以指定或者某个实现类
(2) 实例先定义一个接口LoadBalance, 这里的@SPI作用于接口类上,用来指定默认的实现类标识。
@Adaptive表名该方法会被代理动态实现。
@SPI("demo")
public interface LoadBalance {
@Adaptive
void Hello();
}
定义两个实现类
public class DemoLoadbalance implements LoadBalance {
@Override
public void Hello() {
System.out.println("this is demo balance");
}
}
public class TestLoadBalance implements LoadBalance {
@Override
public void Hello() {
System.out.println("this is test balance");
}
}
在resource/meta-INF/services下创建一个文本文件,文件名为接口的全限定名:
com.lzq.dubbospidemo.service.LoadBalance,并写入以下内容,注意这里和jdk的写法不一样,前面需要写上标识
demo=com.lzq.dubbospidemo.service.impl.DemoLoadbalance test=com.lzq.dubbospidemo.service.impl.TestLoadBalance
开始测试
public static void main(String[] args) {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(LoadBalance.class);
LoadBalance demoBalance = extensionLoader.getExtension("demo");
demoBalance.Hello();
LoadBalance testBalance = extensionLoader.getExtension("test");
testBalance.Hello();
LoadBalance balance = extensionLoader.getDefaultExtension();
balance.Hello();
}
测试结果如图
3. 源码解析
先看一下获取ExtensionLoader的过程
public staticExtensionLoader getExtensionLoader(Class type) { if (type == null) { //不能传入null参数 throw new IllegalArgumentException("Extension type == null"); } else if (!type.isInterface()) { //需要是接口 throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } else if (!withExtensionAnnotation(type)) { //需要带有SPI注解 throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } else { //EXTENSION_LOADERS是一个可以并发访问的map对象,在这里相当于一个缓存 //下面的步骤首先尝试从缓存获取loader对象,若缓存中不存在则新建loader对象 //放入缓存,然后再次从缓存中获取 ExtensionLoader loader = (ExtensionLoader)EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type)); loader = (ExtensionLoader)EXTENSION_LOADERS.get(type); } return loader; } }
下面看看获取具体实现类的过程
public T getExtension(String name) {
if (name != null && name.length() != 0) {
//参数为true,使用默认的extension
if ("true".equals(name)) {
return this.getDefaultExtension();
} else {
//和上面一样,先查看缓存是否有对应的实例对象,没有就新建然后再获取
//这里Holder对象持有一个volatile的value属性,保证了对所有线程的可见性
Holder
创建实例的过程如下:
private T createExtension(String name) {
//这里会先从缓存中查找class对象,如果没有的话就会从配置文件中加载所有的扩展类,最后得到扩展名与对应类的map,详见loadExtensionClasses函数
Class> clazz = (Class)this.getExtensionClasses().get(name);
if (clazz == null) {
throw this.findException(name);
} else {
try {
T instance = EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = EXTENSION_INSTANCES.get(clazz);
}
// 向实例中注入依赖,IOC的实现
this.injectExtension(instance);
Set> wrapperClasses = this.cachedWrapperClasses;
Class wrapperClass;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
// 将当前instance作为参数传给Wrapper的构造方法,并通过反射创建Wrapper实例。
// 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
//这里实际上是AOP的实现
for(Iterator i$ = wrapperClasses.iterator(); i$.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
// 通过含参的构造方法将SPI实例(根据指定名字创建好的)注入进去
// 注入成功并创建好实例之后会把这个组装好的Wrapper实例返回
// 这样循环到下一个Wrapper类时其实注入的是上一个Wrapper类实例
// 这也解释了为什么后定义的先执行
wrapperClass = (Class)i$.next();
}
}
return instance;
} catch (Throwable var7) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + this.type + ") could not be instantiated: " + var7.getMessage(), var7);
}
}
}
//对SPI注解进行解析,从配置文件的目录加载扩展类
private Map> loadExtensionClasses() {
SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + this.type.getName() + ": " + Arrays.toString(names));
}
if (names.length == 1) {
this.cachedDefaultName = names[0];
}
}
}
Map> extensionClasses = new HashMap();
this.loadDirectory(extensionClasses, "meta-INF/dubbo/internal/");
this.loadDirectory(extensionClasses, "meta-INF/dubbo/");
this.loadDirectory(extensionClasses, "meta-INF/services/");
return extensionClasses;
}
这里的IOC是基于setter函数注入依赖来实现
private T injectExtension(T instance) {
try {
if (this.objectFactory != null) {
Method[] arr$ = instance.getClass().getMethods();
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; ++i$) {
Method method = arr$[i$];
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {
//获取setter方法参数类型
Class pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 从 ObjectFactory 中获取依赖对象
Object object = this.objectFactory.getExtension(pt, property);
if (object != null) {
// 通过反射调用 setter 方法设置依赖
method.invoke(instance, object);
}
} catch (Exception var9) {
logger.error("fail to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
}
}
}
}
} catch (Exception var10) {
logger.error(var10.getMessage(), var10);
}
return instance;
}
4.聊聊AOP的实现
参考博客
在上面的源码实现中,涉及到wapper类的处理,dubbo正是基于wapper类来实现wapper,其判断一个类是否为wapper,其实就是判断该类是否含有一个参数类型为SPI接口类型的构造函数
private boolean isWrapperClass(Class> clazz) {
try {
// 尝试取得参数类型为SPI接口类型的构造函数
clazz.getConstructor(this.type);
return true;
} catch (NoSuchMethodException var3) {
return false;
}
}
同时,在上面的循环部分代码中,可以看出当存在多个wapper时,每个wapper被注入的instance对象实际上是上一个wapper。下面通过一个示例来进行演示
-
同样创建一个接口类AopService
@SPI
public interface AopService {
@Adaptive
void service();
}
-
创建两个实现类
public class CppAopService implements AopService {
@Override
public void service() {
System.out.println("this is c++ aop service");
}
}
public class JavaAopService implements AopService {
@Override
public void service() {
System.out.println("this is java aop service");
}
}
-
创建wapper类,用来增强实现类(AOP)
public class AopServiceWapper1 implements AopService {
private AopService aopService;
//必须有这个构造方法才能被判断为wapper类
public AopServiceWapper1(AopService service) {
this.aopService = service;
}
@Override
public void service() {
System.out.println("before wapper1");
aopService.service();
System.out.println("after wapper1");
}
}
public class AopServiceWapper2 implements AopService {
private AopService aopService;
//必须有这个构造方法才能被判断为wapper类
public AopServiceWapper2(AopService service) {
this.aopService = service;
}
@Override
public void service() {
System.out.println("before wapper2");
aopService.service();
System.out.println("after wapper2");
}
}
-
在配置文件中需要加入两个wapper
wapper1=com.lzq.dubboaopdemo.aopservice.Impl.AopServiceWapper1 wapper2=com.lzq.dubboaopdemo.aopservice.Impl.AopServiceWapper2 java=com.lzq.dubboaopdemo.aopservice.Impl.JavaAopService cpp=com.lzq.dubboaopdemo.aopservice.Impl.JavaAopService
-
进行测试
public static void main(String[] args) {
ExtensionLoader loader = ExtensionLoader.getExtensionLoader(AopService.class);
AopService service = loader.getExtension("java");
service.service();
}



