我是靠谱客的博主 生动糖豆,最近开发中收集的这篇文章主要介绍java spi机制_JavaSPI机制学习笔记,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

最近在阅读框架源代码时,常常看到 SPI 的子包, 忍不住查了下: Service Provider Interface : 服务提供接口。

JavaSPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。具体而言:

STEP1. 定义一组接口, 假设是 autocomplete.PrefixMatcher;

STEP2. 写出接口的一个或多个实现(autocomplete.EffectiveWordMatcher, autocomplete.SimpleWordMatcher);

STEP3. 在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件 autocomplete.PrefixMatcher, 内容是要应用的实现类(autocomplete.EffectiveWordMatcher 或 autocomplete.SimpleWordMatcher 或两者);

STEP4. 使用 ServiceLoader 来加载配置文件中指定的实现。

SPI 的应用之一是可替换的插件机制。比如查看 JDBC 数据库驱动包,mysql-connector-java-5.1.18.jar 就有一个 /META-INF/services/java.sql.Driver 里面内容是 com.mysql.jdbc.Driver 。

1437372f2471b4a07de9d5b1d31efa07.png

代码示例:

1. 编写接口和实现类: autocomplete.PrefixMatcher,  autocomplete.EffectiveWordMatcher, autocomplete.SimpleWordMatcher 见 《输入自动提示与补全功能的设计与实现》;

2. 在 src/main/resources/ 下建立文件 /META-INF/services/ autocomplete.PrefixMatcher 填入上述两个类之一或两者都填;

3. 编写测试类。

packageautocomplete;importjava.util.Iterator;importjava.util.ServiceLoader;/*** Created by lovesqcc on 16-2-29.*/

public classPrefixMatcherTest {public static voidmain(String[] args) {

ServiceLoader matcher = ServiceLoader.load(PrefixMatcher.class);

Iterator matcherIter =matcher.iterator();while(matcherIter.hasNext()) {

PrefixMatcher wordMatcher=matcherIter.next();

System.out.println(wordMatcher.getClass().getName());

String[] prefixes= new String[] {"a", "b", "c", "d", "e", "f", "g", "i","l", "n", "p", "r", "s", "t", "v", "w", "do", "finally"};for(String prefix: prefixes) {

System.out.println(wordMatcher.obtainMatchedWords(prefix));

}

}

}

}

要写个 ServiceLoader 的简单实现也不难: 1. 读取配置文件,获取实现类的全名称字符串; 2. 使用 Java 反射机制来构造服务实现类的实例。可以使用泛型方法,避免获取的时候做类型转换。不过 JDK 自带的 java.util.ServiceLoader 实现得更加严谨一些,使用了 ClassLoader 来加载类,并使用迭代器来获取服务实现类。思路大体相同。

packageautocomplete;import java.io.*;importjava.util.ArrayList;importjava.util.List;/*** Created by lovesqcc on 16-2-29.

* A very Simple JavaSPI implementation using java reflection*/

public classSimpleServiceLoader {private static final String PREFIX = "/META-INF/services/";public static List load(Classcls) {

List implClasses =readServiceFile(cls);

List implList = new ArrayList();for(String implClass : implClasses) {

Class c = null;try{

c= (Class) Class.forName(implClass);

implList.add(c.newInstance());

}catch(Exception e) {return new ArrayList();

}

}returnimplList;

}private static List readServiceFile(Class>cls) {

String infName=cls.getCanonicalName();

String fileName= cls.getResource(PREFIX+infName).getPath();try{

BufferedReader br= new BufferedReader(new FileReader(newFile(fileName)));

String line= "";

List implClasses = new ArrayList();while ((line = br.readLine()) != null) {

implClasses.add(line);

}returnimplClasses;

}catch(FileNotFoundException fnfe) {

System.out.println("File not found: " +fileName);return new ArrayList();

}catch(IOException ioe) {

System.out.println("Read file failed: " +fileName);return new ArrayList();

}

}public static voidmain(String[] args) {

List implList = load(PrefixMatcher.class);if (implList != null && implList.size() >0) {for(PrefixMatcher matcher: implList) {

System.out.println(matcher.obtainMatchedWords("sh"));

}

}

}

}

ServiceLoader 的实现涉及到如下概念: 指向对象类型的 Class 对象; 类加载器 ClassLoader; 服务实现类的资源抽象; 服务实现类的全名字符串。结合类加载器和资源抽象获得服务实现类的全名字符串,再通过类加载器获取 Class 对象, 最后通过 Class 对象来构造服务实现类 S 的实例 s 。

ServiceLoader 的实现主要包括:

1.  内部成员包含 service, ClassLoader loader, LinkedHashMap providers, LazyIterator lookupIterator>。其中,service 是服务接口,loader 是类加载器, providers 是服务实现类的缓存, lookupIterator 是获取服务实现类的迭代器,是 ServiceLoader 的内部类;

2. LazyIterator 的内部成员包括 service, ClassLoader loader, Enumeration configs, Iterator pending, String nextName>。 其中,configs 存放服务实现类的资源配置抽象, pending 存放服务实现类的全名字符串, nextName 是下一个可获取的服务实现类的全名字符串。加载资源使用到 classLoader 的 getSystemResources 和 getResources 方法。Java里的资源抽象使用类 URL 来唯一标识,无论是本地文件 ( file:/// ) 还是网络文件 (http(s):// )。由于要从文件或网络读取文本字符串,因此要使用 BufferedReader 。

在 Java 中,Class 和 ClassLoader 是造物之始。万物皆是“某类T” 的存在物,而“某类T” 是“万类之类 Class” 的存在物,类别也是一种存在物,存在物即 Object。实例 t -> 类别 T -> 所有类别的抽象 Class -> Object。要创造类别 T 的实例,先通过某种方式(ClassLoader)找到该物的“种子”(Class 对象),然后通过该种子来创造具体的物 t。要生成一个 Integer 对象,先找到 Class , 然后 newInstance 出 Integer 的实例。而造物也要有个规则,“女娲造物”和“凡人造物”,如果要造一模一样的物种,必须先经由女娲造物,否则就会造成混乱(至少软件中会出现问题)。在 Java 里就有 BootstrapClassLoader -> ExtClassLoader -> AppClassLoader -> CustomClassLoader 的先后规则。关于类加载器可参见 【《Java类加载器总结》,《深入探讨Java类加载器》】, 阅读一个 ClassLoader 的实现。

最后

以上就是生动糖豆为你收集整理的java spi机制_JavaSPI机制学习笔记的全部内容,希望文章能够帮你解决java spi机制_JavaSPI机制学习笔记所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部