深入理解Java 中SPI 制

概述

  • SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是解耦。

SPI整体机制图如下

  • 当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader。

java.util.ServiceLoader

  • 首先,ServiceLoader实现了Iterable接口,所以它有迭代器的属性,这里主要都是实现了迭代器的hasNext和next方法。这里主要都是调用的lookupIterator的相应hasNext和next方法,lookupIterator是懒加载迭代器。
  • 其次,LazyIterator中的hasNext方法,静态变量PREFIX就是”META-INF/services/”目录,这也就是为什么需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件。
  • 最后,通过反射方法Class.forName()加载类对象,并用newInstance方法将类实例化,并把实例化后的类缓存到providers对象中,(LinkedHashMap<String,S>类型) 然后返回实例对象。

demo

//定义一个接口HelloSPI。
package com.vivo.study.spidemo.spi;
public interface HelloSPI {
void sayHello();
} //完成接口的多个实现。
package com.vivo.study.spidemo.spi.impl;
import com.vivo.study.spidemo.spi.HelloSPI;
public class ImageHello implements HelloSPI {
public void sayHello() {
System.out.println("Image Hello");
}
} package com.vivo.study.spidemo.spi.impl;
import com.vivo.study.spidemo.spi.HelloSPI;
public class TextHello implements HelloSPI {
public void sayHello() {
System.out.println("Text Hello");
}
} //在META-INF/services/目录里创建一个以com.vivo.study.spidemo.spi.HelloSPI的文件,这个文件里的内容就是这个接口的具体的实现类。
内容如下
com.vivo.study.spidemo.spi.impl.ImageHello
com.vivo.study.spidemo.spi.impl.TextHello // 使用 ServiceLoader 来加载配置文件中指定的实现
package com.vivo.study.spidemo.test
import java.util.ServiceLoader;
import com.vivo.study.spidemo.spi.HelloSPI;
public class SPIDemo {
public static void main(String[] args) {
ServiceLoader<HelloSPI> serviceLoader = ServiceLoader.load(HelloSPI.class);
// 执行不同厂商的业务实现,具体根据业务需求配置
for (HelloSPI helloSPI : serviceLoader) {
helloSPI.sayHello();
}
}
} //输出结果如下:
Image Hello
Text Hello

不足

  • 1.不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。
  • 2.获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类。
  • 3.多个并发多线程使用 ServiceLoader 类的实例是不安全的。

深入理解Java 中SPI 制的更多相关文章

  1. 深入理解 Java 中 SPI 机制

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/vpy5DJ-hhn0iOyp747oL5A作者:姜柱 SPI(Service Provider ...

  2. 【Java】深入理解Java中的spi机制

    深入理解Java中的spi机制 SPI全名为Service Provider Interface是JDK内置的一种服务提供发现机制,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用 ...

  3. 理解Java中的弱引用(Weak Reference)

    本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...

  4. 深刻理解Java中final的作用(一):从final的作用剖析String被设计成不可变类的深层原因

    声明:本博客为原创博客,未经同意,不得转载!小伙伴们假设是在别的地方看到的话,建议还是来csdn上看吧(原文链接为http://blog.csdn.net/bettarwang/article/det ...

  5. [译]线程生命周期-理解Java中的线程状态

    线程生命周期-理解Java中的线程状态 在多线程编程环境下,理解线程生命周期和线程状态非常重要. 在上一篇教程中,我们已经学习了如何创建java线程:实现Runnable接口或者成为Thread的子类 ...

  6. 深入理解Java中的不可变对象

    深入理解Java中的不可变对象 不可变对象想必大部分朋友都不陌生,大家在平时写代码的过程中100%会使用到不可变对象,比如最常见的String对象.包装器对象等,那么到底为何Java语言要这么设计,真 ...

  7. 深入理解Java中的IO

    深入理解Java中的IO 引言:     对程序语言的设计者来说,创建一个好的输入/输出(I/O)系统是一项艰难的任务 < Thinking in Java >   本文的目录视图如下: ...

  8. 理解Java中的ThreadLocal

    提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...

  9. 深入理解Java中配置环境变量

    深入理解Java中配置环境变量 配置的目的: 本来只在安装JDK的bin目下能运行java.exe,javac.exe,jar.exe,javadoc.exe等Java开发工具包命令,我们现在想让在所 ...

随机推荐

  1. Android5_浅谈Java的package机制

    当代码量越来越大,类越来越多.尤其会增加同名类的风险.所以对类进行管理就显得非常重要. 包(package)机制是java中管理类的重要手段. 包名的命名方式:业内默认的做法是使用公司的网络域名的倒写 ...

  2. IDE介绍之——CLion

    CLion是JetBrains公司旗下发布的一款跨平台C/C++IDE开发工具. 使用CLion上最好要会手写CMake.要先安装编译器套件(一般安装MinGW就行). 对C++标准的支持:基本上Cl ...

  3. Excel基本功能

    公式基础: 比较运算符的种类 flase对应0 而ture对应1 连接运算 利用之前提到的ture就是1 乘以100 注意用括号区分优先级 函数应用基础: 系统已经列好这几个常用的函数 右键单击状态栏 ...

  4. Excel快速填充

    利用单元格右下角填充句柄进行填充 然后选择快速填充 利用数据面板的快速填充功能(ctrl+e) 年月日三列不能同时填充 只能够一列一列的填充 类别取第一列 级别取第二列 但是由于第一个的编号有两个“1 ...

  5. 0013 CSS复合选择器:后代、子代、交集、并集、超链接伪类

    重点: 复合选择器 后代选择器 并集选择器 标签显示模式 CSS背景 背景位置 CSS三大特性 优先级 1. CSS复合选择器 目标 理解 理解css复合选择器分别的应用场景 应用 使用后代选择器给元 ...

  6. Ceph 之RGW Pub-Sub Module

    Overview Pub-Sub module 顾名思义是一个发布订阅相关的模块.Pub-Sub module 为对象存储的变更事件提供一种发布-订阅机制.而发布-订阅架构本身应用非常广泛,如公有云G ...

  7. spring boot中表单验证的使用

    一.前言 为啥子要搞这个表单验证呢?答案简单而现实,举个栗子,你辛辛苦苦的写了一个录入个人信息的功能,比如年龄这个位置,用户就没看到一下子写了个性别男,一提交,直接报错了,是不是很尴尬呢, 作为一个测 ...

  8. 【题解】LOJ6060 Set(线性基)

    [题解]LOJ6060 Set(线性基) orz gql 设所有数的异或和为\(S\),答案是在\(\max (x_1+S\and x_1)\)的前提下\(\min x_1\)输出\(x_1\) 转换 ...

  9. spring之通过注解方式配置Bean(一)

    (1)组件扫描:spring能够从classpath下自动扫描.侦测和实例化具有特定注解的组件. (2)特定组件包括: @Component:基本注解,标识一个受spring管理的组件: @Respo ...

  10. 「算法竞赛进阶指南」0x01 位运算 知识笔记

    二进制是计算机的根本! 你了解她它吗? int lowbit(int x) { return x&(-x);//x&(~x+1),~x=-1-x; } int __builtin_ct ...