前面已经讲过SPI的基本实现原理了,demo也基本实现了,再来说说SPI。
http://aphysia.cn/archives/jdbcspi
背景:SPI是什么?
SPI
,即是Service Provider Interface
,是一种服务提供(接口实现)发现机制,可以通过ClassPath路径下的META-INF/Service
文件查找文件,加载里面定义的类。
一般可以用来启用框架拓展和替换组件,比如在最常见的数据库连接JDBC中,java.sql.Driver
,不同的数据库产商可以对接口做不一样的实现,但是JDK怎么知道别人有哪些实现呢?这就需要SPI
,可以查找到接口的实现,对其进行操作。
用两个字解释:解耦。
再简单点说?
就是Java核心包不知道第三方的包会怎么实现一个接口,定义了一个规则:你要对这个类拓展,那你就把你的实现类配置到一个文件里面,文件名就是你要拓展的接口,这样子,我只要用ServiceLoader
加载接口,我就可以获取到实现类的实例。
对于java核心包来说,我不知道你要怎么实现接口,但是只要你按我说的做,配置好,我就能保证你只要引入你自己的包,我就可以运行到你的代码。
核心代码如下:
ServiceLoader<DBConnectionService> serviceLoader= ServiceLoader.load(DBConnectionService.class);
所以我们此时假设自己对ServiceLoader
已经十分好奇了,这是什么?这是怎么实现的?这么牛逼?
那就看源码?夜深人静刚刚好,白天也看不下去。
这里需要注意的是,这个ServiceLoader
是一个泛型类,实现了Iterable
,说明了什么?说明它的功能有一部分和集合是差不多的,可以将多个服务的实现类加载在里面!!!可以通过遍历的方式,一一取出来
先看看ServiceLoader
的类成员接口,不急着看load()
函数:
public final class ServiceLoader<S>
implements Iterable<S>
{
// 读取配置文件的路径
private static final String PREFIX = "META-INF/services/";
// 加载的服务类或者接口的实现类
private final Class<S> service;
// 类加载器
private final ClassLoader loader;
// 访问控制器
private final AccessControlContext acc;
// 已加载的服务类集合
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// 内部类,真正加载服务类的迭代器
private LazyIterator lookupIterator;
...
}
现在来看load()
函数,其实里面调用的也还是serviceLoader
本身的构造器,两个load方法
- 一个只需要传入需要实现的服务接口
service
- 另一个则是需要同时传入类加载器
loader
// 当前线程的类加载器作为默认加载器
public static <S> ServiceLoader<S> load(Class<S> service) {
// 获取类加载器
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// 调用另外一个加载器
return ServiceLoader.load(service, cl);
}
// 两个参数的加载方法
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
我们还是来看serviceLoader
的构造器:
private ServiceLoader(Class<S> svc, ClassLoader cl) {
// 要加载的接口
service = Objects.requireNonNull(svc, "Service interface cannot be null");
// 加载器,如果为null则默认使用系统加载器进行加载
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
// 控制访问器
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
// 重新加载
reload();
}
看重新加载的方法:
public void reload() {
// 清空已经加载的服务类
providers.clear();
// 初始化查找加载类的迭代器
lookupIterator = new LazyIterator(service, loader);
}
查找加载类的迭代器,到底是什么?从名字来看,是一个懒加载器,就是延迟加载,从名字来看,大概能猜到,这个就是使用的时候才加载,真的是这样么???接着看下去: