博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JDK SPI
阅读量:6705 次
发布时间:2019-06-25

本文共 7421 字,大约阅读时间需要 24 分钟。

简介

SPI全名为Service Provider Interface 作用:为接口自动寻找实现类 实现方式:  1.标准制定者制定接口  2.不同厂商编写针对于该接口的实现类,  并在jar的“classpath:META-INF/services/全接口名称”  文件中指定相应的实现类全类名  3.开发者直接引入相应的jar,就可以实现为接口自动寻找实现类的功能

代码示例

Log接口

public interface Log {    void execute();}复制代码

Log4j类

public class Log4j implements Log {    @Override    public void execute() {        System.out.println("log4j...");    }}复制代码

Logback类

public class Logback implements Log {    @Override    public void execute() {        System.out.println("logback ...");    }}复制代码

Main类

public class Main {    public static void main(String[] args) {        ServiceLoader
serviceLoader = ServiceLoader.load(Log.class); Iterator
iterator = serviceLoader.iterator(); while (iterator.hasNext()) { Log log = iterator.next(); log.execute(); } }}复制代码
在resources下创建META-INF/services目录并创建Log全路径名的文件com.ye.spi.Log文件里写实现类全路径,可以写多个写多个时,可以自己实现逻辑选择对应实现com.ye.spi.Log4j

直接启动Main类就可以得到想要的效果

源码解析

public class Main {    public static void main(String[] args) {        ServiceLoader
serviceLoader = ServiceLoader.load(Log.class); Iterator
iterator = serviceLoader.iterator(); while (iterator.hasNext()) { Log log = iterator.next(); log.execute(); } }}复制代码
1. ServiceLoader.load()2. serviceLoader.iterator()3. iterator.hasNext()4. iterator.next()

第一个方法是ServiceLoader.load()

先看ServiceLoader的属性    private static final String PREFIX="META-INF/services/";//定义实现类的接口文件所在的目录    private final Class service;//接口    private final ClassLoader loader;//定位、加载、实例化实现类    private final AccessControlContext acc;//权限控制上下文    private LinkedHashMap providers = new LinkedHashMap<>();//以初始化的顺序缓存
<接口全名称, 实现类实例>
private LazyIterator lookupIterator;//真正进行迭代的迭代器
public static  ServiceLoader load(Class service) {        ClassLoader cl = Thread.currentThread().getContextClassLoader();        return ServiceLoader.load(service, cl);    }public static  ServiceLoader load(Class service, ClassLoader loader) { return new ServiceLoader<>(service, loader); }复制代码
看上面两段代码,load方法最终是调用传参的构造函数new ServiceLoader<>(service, loader)    再继续看new ServiceLoader<>(service, loader)代码
private ServiceLoader(Class svc, ClassLoader cl) {        service = Objects.requireNonNull(svc, "Service interface cannot be null");        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;        reload();    }public void reload() {        providers.clear();        lookupIterator = new LazyIterator(service, loader);    }private LazyIterator(Class service, ClassLoader loader) {            this.service = service;            this.loader = loader;        }    复制代码
1. 赋值ServiceLoader的service2. 赋值ServiceLoader的loader3. 调用reload方法4. 清空ServiceLoade的providers5. new LazyIterator对象
综上所述,ServiceLoader.load()其实就是初始化了ServiceLoader对象和LazyIterator对象

第二个方法是serviceLoader.iterator()

public Iterator iterator() {        return new Iterator() {            Iterator
> knownProviders = providers.entrySet().iterator(); public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); } public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public void remove() { throw new UnsupportedOperationException(); } }; }复制代码
获取迭代器,   初始化knownProviders,第一次是空的   实现了hasNext方法和next方法

iterator.hasNext()

public boolean hasNext() {        if (knownProviders.hasNext())            return true;        return lookupIterator.hasNext();    }复制代码
先判断knownProviders有没有,如果有就返回true如果没有调用lookupIterator.hasNext()可以认为knownProviders是一个缓存,后面会看到什么时候有值的
先看一下lookupIterator即LazyIterator这个类的属性    Class service;//接口    ClassLoader loader;//类加载器    Enumeration configs = null;//存放配置文件    Iterator pending = null;//存放配置文件中的内容,并存储为ArrayList,即存储多个实现类名称    String nextName = null;//当前处理的实现类名称

LazyIterator.hasNext()方法

public boolean hasNext() {            if (acc == null) {                return hasNextService();            } else {                PrivilegedAction
action = new PrivilegedAction
() { public Boolean run() { return hasNextService(); } }; return AccessController.doPrivileged(action, acc); } }private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; }复制代码
其实调用的是LazyIterator.hasNextService()方法1. nextName如果有,直接返回true2. 找到对应目录文件3. 解析文件内容4. 赋值给nextName5. 返回true

LazyIterator.next()

public S next() {                if (knownProviders.hasNext())                    return knownProviders.next().getValue();                return lookupIterator.next();            }         public S next() {            if (acc == null) {                return nextService();            } else {                PrivilegedAction action = new PrivilegedAction() {                    public S run() { return nextService(); }                };                return AccessController.doPrivileged(action, acc);            }        }           private S nextService() {            if (!hasNextService())                throw new NoSuchElementException();            String cn = nextName;            nextName = null;            Class
c = null; try { c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen } 复制代码
1. 先判断knownProviders有没有,如果有就直接取2. 调用nextService获取3. 通过nextName加载对应的类,即实例化4. 把实例化的对象放入providers中,下次就可以在这里面取,不用在实例化了5. 返回实例化的对象

小结:spi大致流程就讲完了

1. 初始化 ServiceLoader和LazyIterator2. 通过load传入的接口找到对应目录的文件3. 解析文件找到类全路径名4. 通过全路径名加载实例化类5. 放入缓存并返回该对象

转载于:https://juejin.im/post/5c90d14ce51d45206a18befa

你可能感兴趣的文章
[转载]java.lang.OutOfMemoryError: bitmap size exceeds VM budget解决方法
查看>>
SKY IM-A800S 驱动下载
查看>>
应用程序 数据缓存
查看>>
TFS签入签出
查看>>
第二条:遇到多个构造器参数(Constructor Parameters)时要考虑用构建器(Builder)
查看>>
成长,没你想象的那么迫切
查看>>
ASP.NET Core 中文文档 第一章 入门
查看>>
jQuery入门(2)使用jQuery操作元素的属性与样式
查看>>
贴片电阻分类、阻值、功率、封装、尺寸
查看>>
scala+hadoop+spark环境搭建
查看>>
Mqtt协议IOS端移植2
查看>>
Stitching模块中leaveBiggestComponent初步研究
查看>>
使用PrintWriter out=response.getWriter();输出script脚本时乱码解决
查看>>
X.509证书及CeritificationPath及PKCS
查看>>
项目笔记:导出Excel功能设置导出数据样式
查看>>
Python yield 使用
查看>>
【Eclipse】eclipse中设置tomcat启动时候的JVM参数
查看>>
10.查看npm安装信息和版本号
查看>>
国际化环境下系统架构演化
查看>>
Linux系统如何将某一程序设置为开机自启动
查看>>