SPI的全名为Service Provider Interface.普通开发人员可能不熟悉,因为这个是针对厂商或者插件的。在java.util.ServiceLoader的文档里有比较详细的介绍。

简单来说就是通过配置文件指定接口的实现类。

当我们开发一套框架、一套机制、一个插件或一套API时候,如果需要第三方的服务支持,可以直接写死到代码里面,但这种方式耦合太强,不利于切换到其它服务,好的方法是写一个配置文件指定服务的实现方,幸运的是java的spi机制已经帮我们做好了,示例用法如下:

一个接口:

  1. package com.service;
  2.  
  3. public interface IService {
  4. String service();
  5. }

两个实现类:

  1. package com.service;
  2.  
  3. public class ServiceImplA implements IService {
  4. public String service() {
  5. return "invoke ServiceImplA";
  6. }
  7. }
  1. package com.service;
  2.  
  3. public class ServiceImplB implements IService {
  4. public String service() {
  5. return "invoke ServiceImplB";
  6. }
  7. }  

然后创建com.service.IService文件,写入如下内容:

com.service.ServiceImplA
com.service.ServiceImplB

其在Idea中的结构如下:

其中的mazhimazh-1.0-SNAPSHOT.jar是打包后的jar。然后放到Java项目的classpath中。

创建ServiceIterator,代码如下:

  1. package com.test2;
  2.  
  3. import java.lang.reflect.Method;
  4. import java.util.Iterator;
  5.  
  6. import com.service.IService;
  7.  
  8. /**
  9. * Use a service loader appropriate for the platform to provide an
  10. * iterator over annotations processors. If
  11. * java.util.ServiceLoader is present use it, otherwise, use
  12. * sun.misc.Service, otherwise fail if a loader is needed.
  13. */
  14. public class ServiceIterator implements Iterator<IService> {
  15. // The to-be-wrapped iterator.
  16. private Iterator<?> iterator;
  17. private Class<?> loaderClass;
  18. private boolean jusl; // java util service loader
  19. private Object loader;
  20.  
  21. ServiceIterator() {
  22. ClassLoader classLoader = getClass().getClassLoader();
  23. String loadMethodName;
  24. try {
  25. try {
  26. loaderClass = Class.forName("java.util.ServiceLoader");
  27. loadMethodName = "load";
  28. jusl = true;
  29. } catch (ClassNotFoundException cnfe) {
  30. try {
  31. loaderClass = Class.forName("sun.misc.Service");
  32. loadMethodName = "providers";
  33. jusl = false;
  34. } catch (ClassNotFoundException cnfe2) {
  35. // Fail softly if a loader is not actually needed.
  36. return;
  37. }
  38. }
  39.  
  40. // java.util.ServiceLoader.load or sun.misc.Service.providers
  41. // 获得对象所声明的公开方法,在这里获取ServiceLoader的load方法
  42. // 该方法的第一个参数name是要获得方法的名字,第二个参数parameterTypes是按声明顺序标识该方法形参类型。
  43. Method loadMethod = loaderClass.getMethod(loadMethodName,
  44. Class.class,
  45. ClassLoader.class);
  46.  
  47. // 调用方法 public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader)
  48. Object result = loadMethod.invoke(null,
  49. IService.class,
  50. classLoader);
  51.  
  52. // For java.util.ServiceLoader, we have to call another
  53. // method to get the iterator.
  54. if (jusl) {
  55. loader = result; // Store ServiceLoader to call reload later
  56. Method m = loaderClass.getMethod("iterator");
  57. result = m.invoke(result); // serviceLoader.iterator();
  58. }
  59.  
  60. // The result should now be an iterator.
  61. this.iterator = (Iterator<?>) result;
  62. } catch (Throwable t) {
  63. throw new Error(t);
  64. }
  65. }
  66.  
  67. public boolean hasNext() {
  68. try {
  69. return iterator.hasNext();
  70. } catch (Throwable t) {
  71. throw new Error(t);
  72. }
  73. }
  74.  
  75. public IService next() {
  76. try {
  77. return (IService)(iterator.next());
  78. } catch (Throwable t) {
  79. throw new Error(t);
  80. }
  81. }
  82.  
  83. public void remove() {
  84. throw new UnsupportedOperationException();
  85. }
  86.  
  87. public void close() {
  88. if (jusl) {
  89. try {
  90. // Call java.util.ServiceLoader.reload
  91. Method reloadMethod = loaderClass.getMethod("reload");
  92. reloadMethod.invoke(loader);
  93. } catch(Exception e) {
  94. ; // Ignore problems during a call to reload.
  95. }
  96. }
  97. }
  98. }

然后调用,如下:

  1. package com.test2;
  2.  
  3. import java.util.ServiceLoader;
  4. import com.service.IService;
  5.  
  6. public class Test {
  7.  
  8. public static void main(String[] args) {
  9. // 使用1
  10. ServiceIterator s = new ServiceIterator();
  11. while (s.hasNext()) {
  12. IService x = s.next();
  13. System.out.println(x.service());
  14. }
  15. // 使用2
  16. ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class);
  17. for (IService service : serviceLoader) {
  18. System.out.println(service.service());
  19. }
  20. }
  21. }

运行结果如下:

invoke ServiceImplA
invoke ServiceImplB
invoke ServiceImplA
invoke ServiceImplB

  

  

  

  

ServiceLoader解读的更多相关文章

  1. Java之ServiceLoader

    转载请注明源出处:http://www.cnblogs.com/lighten/p/6946683.html 1.简介 JDK1.6之后,java.util包下多了一个类ServiceLoader,其 ...

  2. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  3. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  4. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  5. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  6. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  7. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

  8. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

  9. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

随机推荐

  1. C++中的关键知识点(汇总)

    1. class的virtual 与non-virtual的区别 (1)virtual 函数时动态绑定,而non-virtual是静态绑定,前者是多态效果. (2)多态类的析构函数应该为virtual ...

  2. day 20 02 模块的导入

    day 20 02  模块的导入 1.模块:就是一个文件:放置一些通用的有独立功能程序或者函数.比如建立一个py文件,文件名为:demo,文件里面的内容:demo模块: print('嗨大米')def ...

  3. day15(生成器send方法,递归,匿名函数,内置函数)

    一,复习 ''' 1.带参装饰器 - 自定义 | wraps def wrap(info) def outer1(func): from functools import wraps @wraps(f ...

  4. docker容器日志清理

    1.先查看磁盘空间 df -h 2.找到容器的containerId-json.log文件,并清理(治标不治本,log迟早还会大的) 查看各个容器的log文件大小 find /var/lib/dock ...

  5. Linux中连接mysql执行sql文件

    数据量小的时候可以把sql语句内容粘贴执行,但是文件很大的时候,这样执行效率很慢很慢,需要使用source执行sql文件 1.客户端连接mysql数据库 [root@iZbp1bb2egi7w0uey ...

  6. 日笔记--C# 从数据库取表格到DataGridView---json传输

    只作为个人学习笔记. class OpData { // 创建一个和客户端通信的套接字 Socket socketwatch = null; //连接Access字符串 string strCon; ...

  7. yum 下载RPM包而不进行安装

    yum命令本身就可以用来下载一个RPM包,标准的yum命令提供了--downloadonly(只下载)的选项来达到这个目的. $ sudo yum install --downloadonly < ...

  8. ADB 源码分析(一) ——ADB模块简述【转】

    ADB源码分析(一)——ADB模块简述 1.Adb 源码路径(system/core/adb). 2.要想很快的了解一个模块的基本情况,最直接的就是查看该模块的Android.mk文件,下面就来看看a ...

  9. “全栈2019”Java第九十八章:局部内部类访问作用域成员详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  10. ava Maven项目之Nexus私服搭建和版本管理应用

    目录: Nexus介绍 环境.软件准备 Nexus服务搭建 Java Maven项目版本管理应用 FAQ 1.Nexus介绍 Nexus是一个强大的Maven仓库管理器,它极大地简化了自己内部仓库的维 ...