Java SPI
Posted 水田如雅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java SPI相关的知识,希望对你有一定的参考价值。
what SPI?
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。
如何使用
定义如下类:
public interface SPITestInterface
String consoleSth(String source);
public class DefaultSPITestInterfaceImplOne implements SPITestInterface
@Override
public String consoleSth(String source)
return source;
public class DefaultSPITestInterfaceImplTwo implements SPITestInterface
@Override
public String consoleSth(String source)
if (source == null || source.length() < 1)
return "";
return source.toUpperCase();
之后在resource文件夹下,添加文件:
文件名称为接口的包名,文件内容为:
com.common.lib.demo.javasource.DefaultSPITestInterfaceImplOne
com.common.lib.demo.javasource.DefaultSPITestInterfaceImplTwo
外部调用:
@Test
public void consoleSth()
ServiceLoader<SPITestInterface> testInterfaces = ServiceLoader.load(SPITestInterface.class);
for (SPITestInterface testInterface : testInterfaces)
System.out.println(testInterface.consoleSth("aaaaaBBBB"));;
输出:
aaaaaBBBB
AAAAABBBB
实现解析
首先,扫描的配置文件路径。
public static <S> ServiceLoader<S> load(Class<S> service)
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
加载的时候,会使用当前线程class loader.
构造:
public void reload()
providers.clear();
lookupIterator = new LazyIterator(service, loader);
private ServiceLoader(Class<S> 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();
这里主要是会构造个懒加载器,由这个东西解析文件,并加载类。
在懒加载器里面:
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());
providers.put(cn, p);
return p;
catch (Throwable x)
fail(service,
"Provider " + cn + " could not be instantiated",
x);
throw new Error(); // This cannot happen
实例化的类都会放入:
// Cached providers, in instantiation order
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
总结
- 不需要改动源码就可以实现扩展,解耦。
- 实现扩展对原来的代码几乎没有侵入性。
- 只需要添加配置就可以实现扩展,符合开闭原则。
这种机制常用在,例如,java提供一些接口,例如,java提供数据访问的接口,我们引入mysql connect的包,来实现对mysql的访问操作,在mysql-connector中,加入对mysql数据库访问的实现,就是通过SPI实现的。
这种可插拔形式,在架构设计中应用广泛。
以上是关于Java SPI的主要内容,如果未能解决你的问题,请参考以下文章
java SPI 01-SPI 是什么?spi 使用入门教程 ServiceLoader 使用简介