1.什么是java的spi

spi 全称为 service provider interface 即 服务提供接口,用来作为服务的扩展发现。在运行时动态添加接口的实现,是对接口的实现类的创建管理。

2.运用场景

类似于spring dubbo都有基于java自带的spi模式实现的自有spi管理的机制。

3.如何使用

  • java 自带的spi

        核心类是:java.util.ServiceLoader 该类是一个被final修饰的类
        使用:
        1.在jar包的"src/META-INF/services"目录下建立一个文件,该文件的文件名是对应接口的全限定名,文件的内容可以有多行,每行是该接口对应的具体实现类的全限定名。

private static final String PREFIX = "META-INF/services/";
 
        2.在代码中使用java.util.ServiceLoader加载对应的接口类
//指定类加载器
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
//使用当前线程的类加载器
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
} //使用extension class loader
public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
ClassLoader prev = null;
while (cl != null) {
prev = cl;
cl = cl.getParent();
}
return ServiceLoader.load(service, prev);
}

3.  调用ServiceLoader.load()方法后会new 一个 ServiceLoader<>(Class service, loader);

private ServiceLoader(Class<S> svc, ClassLoader cl) {
// 这里的service 是对应的接口
service = Objects.requireNonNull(svc, "Service interface cannot be null");
// 类加载器
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
//获取当前的AccessControlContext 默认为空
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}

  这里的acc 是 java的安全管理器

// The access control context taken when the ServiceLoader is created
private final AccessControlContext acc;
  对应的System.getSecurityManager() 获取当前的安全SecurityManager
java.lang.System类中

private static volatile SecurityManager security = null;

public static SecurityManager getSecurityManager() {
return security;
}

4.接着调用reload方法

public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}

  providers 同lookupInterator

// Cached providers, in instantiation order
private LinkedHashMap<String,S> providers = new LinkedHashMap<>(); // The current lazy-lookup iterator
private LazyIterator lookupIterator;

5.new 一个内部类 懒加载类LazyInterator

private class LazyIterator
implements Iterator<S>
{ Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null; private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
} 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;
} 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
} public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
} public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
} public void remove() {
throw new UnsupportedOperationException();
} }

6.使用迭代器Interator取出接口的实现对象

public Iterator<S> iterator() {
return new Iterator<S>() { Iterator<Map.Entry<String,S>> 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();
} };
}

java的spi(Service Provider Interface)机制及源码(java8)的更多相关文章

  1. SPI(Service Provider Interface)机制

    JAVA SPI 约定如下:当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/ 目录中同时创建一个以服务接口命名的文件,该文件中的内容就是实现该服务接口的具体 ...

  2. JAVA SPI(Service Provider Interface)原理、设计及源码解析(其一)

    背景 团队内部轮流技术分享,其他人都是分享源码,我每次都是设计和架构,感觉自己太特立独行.这次我要合群点,分享点源码. 概念 Service Provider Interface:服务提供方接口.是一 ...

  3. Java SPI(Service Provider Interface)简介

    SPI 简介 SPI 全称为(Service Provider Interface),是JDK内置的一种服务提供发现机制. 一个服务(Service)通常指的是已知的接口或者抽象类,服务提供方就是对这 ...

  4. Java SPI(Service Provider Interface)

    SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制. 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现的机制, 举个 ...

  5. SPI: Service Provider Interface

    Service Provider Interface: JDK提供的一种服务发现的机制:主要是用于厂商实现JDK的只用. 比如说打印机,JDK提供了一个驱动接口com.printl.printerDr ...

  6. Java中的SPI(Service Provider Interface)

    转自:http://singleant.iteye.com/blog/1497259 最近看到公司的一些框架和之前看到的开源的一些框架的一些服务发现和接入都采用了java的spi机制. 所以简单的总结 ...

  7. SPI(Service Provider Interface)--通过接口获取服务

    spi 现在已有实现 jdk 提供实现 dubbo里的spi实现 一.jdk实现 配置 定义接口 定义实现类 配置资源文件 classpath下创建(META-INF/services/接口全面:ME ...

  8. Service Provider Interface

    @(Java)[SPI] Service Provider Interface API的一种设计方法,一般用于一些服务提供给第三方实现或者扩展,可以增强框架的扩展或者替换一些组件. 结构 Servic ...

  9. 【Java实战】源码解析Java SPI(Service Provider Interface )机制原理

    一.背景知识 在阅读开源框架源码时,发现许多框架都支持SPI(Service Provider Interface ),前面有篇文章JDBC对Driver的加载时应用了SPI,参考[Hibernate ...

随机推荐

  1. python-条件判断

    条件判断 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. 比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现: age = 20 if age >= ...

  2. ps:图像尺寸

    在课程#01中我们知道了显示器上的图像是由许多点构成的,这些点称为像素,意思就是“构成图像的元素”.但是要明白一点:像素作为图像的一种尺寸,只存在于电脑中,如同RGB色彩模式一样只存在于电脑中.像素是 ...

  3. gcc的-D,-w,-W,-Wall,-O3这些参数的意义

    一.-D 其意义是添加宏定义,这个很有用. 当你想要通过宏控制你的程序,不必傻乎乎的在程序里定义,然后需要哪个版本,去修改宏. 只需要在执行gcc的时候,指定-D,后面跟宏的名称即可. 示例: gcc ...

  4. php 输出缓冲

    <?php ob_start();//开启php输出缓冲区 echo "A"; //"A"会进入php输出缓冲区 ob_flush();//将php输出缓 ...

  5. 【串线篇】spring boot日志框架

    一.日志框架 小张:开发一个大型系统: 1.System.out.println(""):将关键数据打印在控制台:去掉?写在一个文件? 2.框架来记录系统的一些运行时信息:日志框架 ...

  6. 20.Nodejs基础知识(上)——2019年12月16日

    2019年12月16日18:58:55 2019年10月04日12:20:59 1. nodejs简介 Node.js是一个让JavaScript运行在服务器端的开发平台,它让JavaScript的触 ...

  7. Test 6.24 T1 购物

    问题描述 小 C 今天出去购物,商店里总共有 n 种商品,小 C 的钱够买至多 k 个商品. 小 C 对每种商品都有一个喜爱程度,但如果买了同一种商品很多次,小 C 对这种商品的喜爱程度就会降低. 具 ...

  8. 【leetcode】449. Serialize and Deserialize BST

    题目如下: Serialization is the process of converting a data structure or object into a sequence of bits ...

  9. Java缓冲字符读取

    public class BufferedReaderDemo { public static void main(String[] args) throws IOException { // 创建流 ...

  10. php面试专题---7、文件及目录处理考点

    php面试专题---7.文件及目录处理考点 一.总结 一句话总结: 用脑子:基本文件操作和目录操作了解一波,不必强求 1.不断在文件hello.txt头部写入一行“Hello World”字符串,要求 ...