JavaSPI机制学习笔记
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSPI机制学习笔记相关的知识,希望对你有一定的参考价值。
最近在阅读框架源代码时,常常看到 SPI 的子包, 忍不住查了下: Service Provider Interface : 服务提供接口。
JavaSPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。具体而言: 1. 定义一组接口, 假设是 autocomplete.PrefixMatcher; 2. 写出接口的一个或多个实现(autocomplete.EffectiveWordMatcher, autocomplete.SimpleWordMatcher); 3. 在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件 autocomplete.PrefixMatcher, 内容是要应用的实现类(autocomplete.EffectiveWordMatcher 或 autocomplete.SimpleWordMatcher 或两者); 4. 使用 ServiceLoader 来加载配置文件中指定的实现。
SPI 的应用之一是可替换的插件机制。比如查看 JDBC 数据库驱动包,mysql-connector-java-5.1.18.jar 就有一个 /META-INF/services/java.sql.Driver 里面内容是 com.mysql.jdbc.Driver 。
代码示例:
1. 编写接口和实现类: autocomplete.PrefixMatcher, autocomplete.EffectiveWordMatcher, autocomplete.SimpleWordMatcher 见 《输入自动提示与补全功能的设计与实现》;
2. 在 src/main/resources/ 下建立文件 /META-INF/services/ autocomplete.PrefixMatcher 填入上述两个类之一或两者都填;
3. 编写测试类。
package autocomplete; import java.util.Iterator; import java.util.ServiceLoader; /** * Created by lovesqcc on 16-2-29. */ public class PrefixMatcherTest { public static void main(String[] args) { ServiceLoader<PrefixMatcher> matcher = ServiceLoader.load(PrefixMatcher.class); Iterator<PrefixMatcher> 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 来加载类,并使用迭代器来获取服务实现类。思路大体相同。
package autocomplete; import java.io.*; import java.util.ArrayList; import java.util.List; /** * Created by lovesqcc on 16-2-29. * A very Simple JavaSPI implementation using java reflection */ public class SimpleServiceLoader { private static final String PREFIX = "/META-INF/services/"; public static <T> List<T> load(Class<T> cls) { List<String> implClasses = readServiceFile(cls); List<T> implList = new ArrayList<T>(); for (String implClass : implClasses) { Class<T> c = null; try { c = (Class<T>) Class.forName(implClass); implList.add(c.newInstance()); } catch (Exception e) { return new ArrayList<T>(); } } return implList; } private static List<String> readServiceFile(Class<?> cls) { String infName = cls.getCanonicalName(); String fileName = cls.getResource(PREFIX+infName).getPath(); try { BufferedReader br = new BufferedReader(new FileReader(new File(fileName))); String line = ""; List<String> implClasses = new ArrayList<String>(); while ((line = br.readLine()) != null) { implClasses.add(line); } return implClasses; } catch (FileNotFoundException fnfe) { System.out.println("File not found: " + fileName); return new ArrayList<String>(); } catch (IOException ioe) { System.out.println("Read file failed: " + fileName); return new ArrayList<String>(); } } public static void main(String[] args) { List<PrefixMatcher> implList = load(PrefixMatcher.class); if (implList != null && implList.size() >0) { for (PrefixMatcher matcher: implList) { System.out.println(matcher.obtainMatchedWords("sh")); } } } }
以上是关于JavaSPI机制学习笔记的主要内容,如果未能解决你的问题,请参考以下文章