java spi 详解_java SPI详解

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。我们先通过一个很简单的例子来看下它是怎么用的。

一、使用

先创建一个父类:

/**

* 父类

*/

public interface SPIService {

void execute();

}

子类1:

/**

* 子类1

*/

public class SPIImpl1 implements SPIService{

@Override

public void execute() {

System.out.println("spi1 execute()");

}

}

子类2:

/**

* 子类2

*/

public class SPIImpl2 implements SPIService {

@Override

public void execute() {

System.out.println("spi2 execute()");

}

}

在resource目录下创建我们需要使用的文件夹和文件

65cd1c56c6166b81ac64b859ba8c3ddc.png

好了,下面就是我们的测试类了

public class Test {

public static void main(String[] args) {

//第一种方式,获取SPIService的子类

Iterator providers = Service.providers(SPIService.class);

while (providers.hasNext()){

SPIService service = providers.next();

service.execute();

}

//第二种方式,获取SPIService的子类

ServiceLoader load = ServiceLoader.load(SPIService.class);

Iterator iterable = load.iterator();

while (iterable.hasNext()){

SPIService service = iterable.next();

service.execute();

}

}

}

打印日志如下:

33c44a2ad4e926824ad224cdb96d919d.png

这样就算是完成了第一步的简单使用

二、原理

我们先讲第二种方法吧

ServiceLoader load = ServiceLoader.load(SPIService.class);

ServiceLoader实现了Iterable

public final class ServiceLoader implements Iterable

/**

* 从当前线程中获得类加载器,这时候获得的是applicationClassLoader

**/

public static ServiceLoader load(Class service) {

ClassLoader cl = Thread.currentThread().getContextClassLoader();

return ServiceLoader.load(service, cl);

}

/**

* 初始化ServiceLoader,如果loader为null,那么使用Systenm class Loader,如果失败则使用BootStrap Class Loader

**/

public static ServiceLoader load(Class service,

ClassLoader loader)

{

return new ServiceLoader<>(service, loader);

}

/**

* 如果loader为null,那么使用Systenm class Loader,如果失败则使用BootStrap Class Loader

* 给acc设值,SecutityManager,目前还不清楚这个做什么用的

**/

private ServiceLoader(Class svc, ClassLoader cl) {

service = Objects.requireNonNull(svc, "Service interface cannot be null");

loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;

acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;

//去除缓存,目前未知具体做啥的

reload();

}

/**

* Clear this loader's provider cache so that all providers will be

* reloaded.

*

*

After invoking this method, subsequent invocations of the {@link

* #iterator() iterator} method will lazily look up and instantiate

* providers from scratch, just as is done by a newly-created loader.

*

*

This method is intended for use in situations in which new providers

* can be installed into a running Java virtual machine.

*/

public void reload() {

providers.clear();

lookupIterator = new LazyIterator(service, loader);

}

因为ServiceLoader实现了Iterable 所以当调用hasNext方法时,走了ServiceLoader自己的

public boolean hasNext() {

if (acc == null) {

return hasNextService();

} else {

PrivilegedAction action = new PrivilegedAction() {

public Boolean run() { return hasNextService(); }

};

return AccessController.doPrivileged(action, acc);

}

}

在acc为null的情况下

private boolean hasNextService() {

if (nextName != null) {

return true;

}

if (configs == null) {

try {

String fullName = PREFIX + service.getName();

if (loader == null)

//获得系统资源,通过SystemClassLoader或者BootstrapClassLoader来加载系统资源

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;

}

上文中的parse方法就是把类名从文件中一行行解析出来

private Iterator parse(Class> service, URL u)

throws ServiceConfigurationError

{

InputStream in = null;

BufferedReader r = null;

ArrayList names = new ArrayList<>();

try {

in = u.openStream();

r = new BufferedReader(new InputStreamReader(in, "utf-8"));

int lc = 1;

while ((lc = parseLine(service, u, r, lc, names)) >= 0);

} catch (IOException x) {

fail(service, "Error reading configuration file", x);

} finally {

try {

if (r != null) r.close();

if (in != null) in.close();

} catch (IOException y) {

fail(service, "Error closing configuration file", y);

}

}

return names.iterator();

}

下面就是通过next()方法获得类对象

public S next() {

if (acc == null) {

return nextService();

} else {

PrivilegedAction action = new PrivilegedAction() {

public S run() { return nextService(); }

};

return AccessController.doPrivileged(action, acc);

}

}

nextService()去实例化此类

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 {

//把对象强转为接口类型

S p = service.cast(c.newInstance());

//provider暂时缓存,所以在初始化的时候要调用clear方法,清空此缓存

providers.put(cn, p);

return p;

} catch (Throwable x) {

fail(service,

"Provider " + cn + " could not be instantiated",

x);

}

throw new Error(); // This cannot happen

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值