java spi接口定义_Java SPI

一、简介

SPI全称Service Provider Interface,它是JDK内置的一种可以动态发现服务的机制。通过这种方式,可以方便地将服务提供者与第三方实现客户端解耦。它主要包含三个基本组件:服务接口,提供者注册API以及服务访问API。

二、使用场景

Java SPI实际上就是“面向接口编程+策略模式+配置文件”组合实现动态加载机制,多用于各种框架中,通过暴露扩展点,实现对框架特定的点进行定制,提升框架灵活性。

1. SPI应用实例Java SQL Driver(MySQL, Oracle, SqlServer)

SLF4J(log4j, logback)

Dubbo

Spring(Converter, Formatter)

三、示例

1. 定义一个接口public interface ProgramLanguage {

String hello();

}

2. 编写接口实现类public class Chinese implements Language {

@Override

public String hello() {

return "你好";

}

}

public class English implements Language {

@Override

public String hello() {

return "Hello";

}

}

3. 配置文件

创建/resources/META-INF/services,并在services文件夹下创建以接口全限定名称命名的文件,并在文件中写入实现类的全限定名com.example.spi.api.impl.English

com.example.spi.api.impl.Chinese

4. 测试public class App {

public static void main(String[] args) {

ServiceLoader loader = ServiceLoader.load(Language.class);

for (Language language : loader) {

System.out.println(language.hello());

}

}

}

输出

完整代码放在GitHub

四、原理

首先看下ServiceLoader继承关系,从这里看出为什么我们可以对loader对象进行foreach操作

接下来看iterator方法,返回了一个Iterator实例private LinkedHashMap providers = new LinkedHashMap<>();

private LazyIterator lookupIterator;

public Iterator iterator() {

return new Iterator() {

Iterator> knownProviders

= providers.entrySet().iterator();

public boolean hasNext() {

// 缓存已加载的实例

if (knownProviders.hasNext())

return true;

// 查找实例

return lookupIterator.hasNext();

}

public S next() {

if (knownProviders.hasNext())

return knownProviders.next().getValue();

return lookupIterator.next();

}

public void remove() {

throw new UnsupportedOperationException();

}

};

}

继续跟踪lookupIterator.hasNext()public boolean hasNext() {

if (acc == null) {

// 查找Service

return hasNextService();

} else {

PrivilegedAction action = new PrivilegedAction() {

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

};

return AccessController.doPrivileged(action, acc);

}

}

private static final String PREFIX = "META-INF/services/";

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;

}

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();

}

从上面的代码可以看出,ServiceLoader读取文件的内容,并尝试将读取到的每一行加载对应类,并实例化对象,这样便完成了服务查找过程。

注意,由于使用这里通过newInstance()方法实例化对象(即通过默认构造方法创建对象),所以Service必须保留默认构造方法。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值