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

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

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

一个接口:

package com.service;

public interface IService {
    String service();
}

两个实现类:

package com.service;

public class ServiceImplA implements IService {
    public String service() {
        return "invoke ServiceImplA";
    }
}
package com.service;

public class ServiceImplB implements IService {
    public String service() {
        return "invoke ServiceImplB";
    }
}  

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

com.service.ServiceImplA
com.service.ServiceImplB

其在Idea中的结构如下:

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

创建ServiceIterator,代码如下:

package com.test2;

import java.lang.reflect.Method;
import java.util.Iterator;

import com.service.IService;

/**
 * Use a service loader appropriate for the platform to provide an
 * iterator over annotations processors.  If
 * java.util.ServiceLoader is present use it, otherwise, use
 * sun.misc.Service, otherwise fail if a loader is needed.
 */
public  class ServiceIterator implements Iterator<IService> {
    // The to-be-wrapped iterator.
    private Iterator<?> iterator;
    private Class<?> loaderClass;
    private boolean jusl; // java util service loader
    private Object loader;

    ServiceIterator() {
    	 ClassLoader classLoader = getClass().getClassLoader();
        String loadMethodName;
        try {
            try {
                loaderClass = Class.forName("java.util.ServiceLoader");
                loadMethodName = "load";
                jusl = true;
            } catch (ClassNotFoundException cnfe) {
                try {
                    loaderClass = Class.forName("sun.misc.Service");
                    loadMethodName = "providers";
                    jusl = false;
                } catch (ClassNotFoundException cnfe2) {
                    // Fail softly if a loader is not actually needed.
                    return;
                }
            }

            // java.util.ServiceLoader.load or sun.misc.Service.providers
            // 获得对象所声明的公开方法,在这里获取ServiceLoader的load方法
            // 该方法的第一个参数name是要获得方法的名字,第二个参数parameterTypes是按声明顺序标识该方法形参类型。
            Method loadMethod = loaderClass.getMethod(loadMethodName,
                    Class.class,
                    ClassLoader.class);

            // 调用方法 public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader)
            Object result = loadMethod.invoke(null,
            		IService.class,
                    classLoader);

            // For java.util.ServiceLoader, we have to call another
            // method to get the iterator.
            if (jusl) {
                loader = result; // Store ServiceLoader to call reload later
                Method m = loaderClass.getMethod("iterator");
                result = m.invoke(result); // serviceLoader.iterator();
            }

            // The result should now be an iterator.
            this.iterator = (Iterator<?>) result;
        } catch (Throwable t) {
            throw new Error(t);
        }
    }

    public boolean hasNext() {
        try {
            return iterator.hasNext();
        } catch (Throwable t) {
            throw new Error(t);
        }
    }

    public IService next() {
        try {
            return (IService)(iterator.next());
        } catch (Throwable t) {
            throw new Error(t);
        }
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    public void close() {
        if (jusl) {
            try {
                // Call java.util.ServiceLoader.reload
                Method reloadMethod = loaderClass.getMethod("reload");
                reloadMethod.invoke(loader);
            } catch(Exception e) {
                ; // Ignore problems during a call to reload.
            }
        }
    }
}

然后调用,如下:

package com.test2;

import java.util.ServiceLoader;
import com.service.IService;

public class Test {

	public static void main(String[] args) {
		// 使用1
		ServiceIterator s = new ServiceIterator();
		while (s.hasNext()) {
			IService x = s.next();
			System.out.println(x.service());
		}
		// 使用2
		ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class);
		for (IService service : serviceLoader) {
			System.out.println(service.service());
		}
	}
}

运行结果如下:

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. [转]快速入门系列--WebAPI--01基础

    本文转自:http://www.cnblogs.com/wanliwang01/p/aspnet_webapi_base01.html ASP.NET MVC和WebAPI已经是.NET Web部分的 ...

  2. B-spline Curves 学习之B样条曲线的移动控制点、修改节点分析(7)

    B-spline Curves: Moving Control Points 本博客转自前人的博客的翻译版本,前几章节是原来博主的翻译内容,但是后续章节博主不在提供翻译,后续章节我在完成相关的翻译学习 ...

  3. 编译语言 vs 解释语言

    编译语言 vs 解释语言 阅读:  评论:  作者:Rybby  日期:  来源:rybby.com 一直以为,编译语言的性能绝对比解释语言快,因为就理论而言,解释语言要一边解释(将脚本语言翻译成计算 ...

  4. 简单配置vps,防ddos攻击

    防人之心不可无. 网上总有些无聊或者有意的人.不多说了.上干货,配置vps apf防小流量ddos攻击. 对于大流量的ddos攻击, 需要机房的硬件防火墙,vps内部可能也扛不住. 1. 安装 DDo ...

  5. 使用python登录CNZZ访问量统计网站,然后获取相应的数据

    思路: 第一步:使用pypeteer.launcher打开浏览器, 第二步:向CNZZ的登录(通过使用iframe嵌入的阿里巴巴单点登录页面),向iframe页面中自动输入用户名和密码,然后点击登录按 ...

  6. ASP.NET Core IdentityServer4 新手上路

    OAuth2.0资料 今天看到一篇博主写了该系列文章,贴图和过程都比较详细,俗话说实践是检验真理的唯一标准(如果是按照参考文章复制粘贴,应该不会出现踩坑,但是我喜欢自己手动敲一遍),发现几个坑,因而总 ...

  7. 了解eslint

    1.简介:eslint检查我们写的 JavaScript 代码是否满足指定规则的静态代码检查工具. JSHint 和 JSLint 也是静态代码检查工具,但伴随着发展,他们已经无法满足需求,于是ESl ...

  8. 关于一些blog优化

    有很多的好看的$java\ script$ 可以大大的增加$blog$的好看度. 这里,本宝宝就列举几个 upd:不定期更新 1.有木有觉得背景的小姐姐和雪花特效极其的配啊啊啊!!! 页面定制CSS插 ...

  9. LOJ#2190. 「SHOI2014」信号增幅仪(最小圆覆盖)

    题面 传送门 题解 我连椭圆是个啥都不知道导致这么简单一道题我一点思路都没有-- 我们把坐标系旋转一下,让半长轴成为新的\(x\)轴,也就是说所有点都绕原点逆时针旋转\(360-a\)度,然后再把所有 ...

  10. 如果你最近在考虑OCR的问题,请进来~~~

    本文主要是python方面各类ocr的api对比问题,至于app推荐几款:合合信息(扫面全能王),TextGrabber,白描等等等等 工作需要,搞文字识别技术,对比了几家 百度的OCR: #!/us ...