java插件式框架_浅谈 java spi 如何帮助框架插件化

spi 简介

spi 的全称是Service Provider Interface,主要作用是在让服务具备运行时加载接口的指定实现类的能力,java从 1.6 开始提供此机制(其实 1.3 开始就有了,只不过一直自嗨内部使用,没暴露外部方法给大家用而已),而各种框架有时也自己实现此机制以增强一些特有的功能(e.g:dubbo自己实现的 spi,spring-boot 类似的有spring factories)。

spi通常应用在框架中,辅助框架实现功能的插件化,让用户自己按照约定写的功能也能被框架加载运行。下面就举个 java spi 的例子,从例子中感受spi 的用法以及如何帮助框架实现插件化

java spi demo

ps:以下 demo 可以自行下载

git clone git@github.com:likemoongg/blog-code-demo.git

为了说明spi 的具体用途,在这里举个例子:为了实现通用的字典查询功能可以开发一种框架,可实现对各种不同字典的查询,字典可以由用户或第三方包定制。

框架侧代码

字典类的接口Dictionary

package dictionary.spi;

public interface Dictionary {

public String getDefinition(String word);

}

查询字典的服务DictionaryService

package dictionary;

import dictionary.spi.Dictionary;

import java.util.Iterator;

import java.util.ServiceConfigurationError;

import java.util.ServiceLoader;

public class DictionaryService {

private static DictionaryService service;

private ServiceLoader loader;

private DictionaryService() {

// 这里的 ServiceLoader 就是 java 原生 spi 的重要入口类

// 其入参是一个接口,返回的 ServiceLoader loader 可以加载出其实现类的实例

loader = ServiceLoader.load(Dictionary.class);

}

// 实现单例

public static synchronized DictionaryService getInstance() {

if (service == null) {

service = new DictionaryService();

}

return service;

}

// 扫描 目录META-INF/services/dictionary.spi.Dictionary下描述的所有的 Dictionary 的实现类,依次查找入参的 word

// 其中目录层级META-INF/services是 java spi 默认约定的目录

public String getDefinition(String word) {

String definition = null;

try {

// 利用 ServiceLoader loader 的迭代器遍历每一个实现类,寻找可以识别 word的字典

Iterator dictionaries = loader.iterator();

while (definition == null && dictionaries.hasNext()) {

Dictionary d = dictionaries.next();

definition = d.getDefinition(word);

}

} catch (ServiceConfigurationError serviceError) {

definition = null;

serviceError.printStackTrace();

}

return definition;

}

}

以上两个类就是框架提供的所有类了,我们可以看到框架还没有提供注释里所说的 META-INF/services/dictionary.spi.Dictionary 文件啊!用户运行起来肯定有问题呀!!

这个目录就是 spi 机制的关键也是我们能实现插件化的重要一步。从上面可以看到框架并没有和具体的某个实现类捆绑起来,而是通过 ServiceLoader 去寻找具体的实现类,接口对应哪些实现类就是 META-INF/services/dictionary.spi.Dictionary 文件所描述的关键信息啦。所以用户可以自己编写该文件,就相当于指定了 Dictionary 的实现类具体有哪些,也就实现了插件化!

用户侧的程序

使用框架的用户自己编写的具体Dictionary实现类有两个,一个汉语词典EnDictionary,一个汉语词典CnDictionary。

那么 META-INF/services/dictionary.spi.Dictionary 可以这么写(其实就是类的全限定名称啦~)

net.likemoon.dictionary.EnDictionary

net.likemoon.dictionary.CnDictionary

两个具体实现类如下

package net.likemoon.dictionary;

public class EnDictionary implements dictionary.spi.Dictionary{

@Override

public String getDefinition(String word){

if (word.matches("[a-zA-Z]+")) {

return "looking up English dictionary...";

}

return null;

}

}

public class CnDictionary implements Dictionary {

@Override

public String getDefinition(String word){

if (!word.matches("[a-zA-Z]+")) {

return "正在查阅中文字典...";

}

return null;

}

}

最后是完整的用户使用框架的的例子

package net.likemoon;

import dictionary.DictionaryService;

public class lookupDictionary {

public static void main(String[] args) {

DictionaryService dictionaryService = DictionaryService.getInstance();

System.out.println(dictionaryService.getDefinition("english"));

System.out.println(dictionaryService.getDefinition("中文"));

}

}

输出

looking up English dictionary...

正在查阅中文字典...

总结

上面的demo 中,框架始终只面向 Dictionary 接口编写功能。具体的字典实现类由java spi 获取。而用户可以在不侵入框架代码的情况下,通过编写约定的描述文件,让框架加载用户自己编写的实现类,扩展功能实现插件化。

当然除了用户(业务程序员)自己编写以外,引入第三方编写的插件 jar 包也可以实现相同的扩展效果。此时第三方的 jar包需包含:扩展的实现类+服务描述文件

可见 spi 的核心机制是接口到具体实现类的关联由一个文件描述。

总结 spi 的服务描述文件的要素。

1、文件通常要放在某个约定的目录(上面 demo 中使用的java spi 规定的就是META-INF/services)

2、文件名和文件内容要体现接口和实现类的关联(java spi 中该文件名需为接口名,文件内容为实现类全限定名)

3、文件可以编写多份,都能被相关加载类加载(这样框架可以有内置的实现类,同时用户和第三方扩展包可以加入自己写的实现类扩展功能)

具体的 spi 实现过程中还有很多细节,比如要大量使用Map 做缓存以加快非首次访问的速度,比如接口的实现类都需要提供无参数构造器方便进行实例化等等。

后续会具体讲解一下 dubbo 的 spi 的实现过程和诸多细节。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值