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. mysql中,表与表之间的关系

     """ 1.字段的修改.添加.删除 2.多表关系(外键) 3.单表详细操作:增删改,查(各种条件) """ 字段操作  create ta ...

  2. linux下查看Apache的访问日志及ip

    linux下查看Apache的实时访问日志:tail -f  /etc/httpd/logs/access_log 查看有哪些ip访问过:cat access_log |awk '{print $1} ...

  3. 监听table滚动事件,滚动到底部时加载数据

    mounted() { this.$refs.scrollTable.addEventListener( 'scroll',(event) => { this.getDistance(event ...

  4. 百度地图api 实例 自动提示 并计算两地的行驶距离

    百度地图api 实例 自动提示 并计算两地的行驶距离 <!DOCTYPE html> <html> <head> <meta http-equiv=" ...

  5. HDU 4678 Mine(博弈)

    Mine Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submi ...

  6. 【LeetCode】二分 binary_search(共58题)

    [4]Median of Two Sorted Arrays [29]Divide Two Integers [33]Search in Rotated Sorted Array [34]Find F ...

  7. 【leetcode】1030. Matrix Cells in Distance Order

    题目如下: We are given a matrix with R rows and C columns has cells with integer coordinates (r, c), whe ...

  8. php对象转数组的函数

    关于php中想让对象以数组的形式访问,这时候就需要使用到get_object_vars()函数了.先来介绍一下这个函数. 官方文档是这样解释的: 1 array get_object_vars ( o ...

  9. 退役——halfrot's life in OI

    这是一个没有人看的博客里丢了两年的坑,还有很多事应该做,但是我很懒,所以今天把它填了. 前记:和很多人的竞赛生涯一样,一开始我也是奋不顾身,奔月而去,然而身处弱校,没有人引导方向,再加上自己很蒻的主要 ...

  10. 【Java】SpringBoot的bean懒加载@Lazy注解

    注解说明 @Lazy:一般情况下,Spring容器在启动时会创建所有的Bean对象,使用@Lazy注解可以将Bean对象的创建延迟到第一次使用Bean的时候. 引用 在类上加入@Lazy或者@Lazy ...