惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
Arctic Wolf
博客园 - 聂微东
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
小众软件
小众软件
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园_首页
L
LangChain Blog
A
About on SuperTechFans
阮一峰的网络日志
阮一峰的网络日志
I
Intezer
T
The Blog of Author Tim Ferriss
Security Latest
Security Latest
C
CXSECURITY Database RSS Feed - CXSecurity.com
Know Your Adversary
Know Your Adversary
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Palo Alto Networks Blog
Scott Helme
Scott Helme
S
Secure Thoughts
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
Attack and Defense Labs
Attack and Defense Labs
P
Privacy & Cybersecurity Law Blog
O
OpenAI News
H
Heimdal Security Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Help Net Security
Help Net Security
C
Cyber Attacks, Cyber Crime and Cyber Security
Blog — PlanetScale
Blog — PlanetScale
GbyAI
GbyAI
G
Google Developers Blog
博客园 - Franky
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
K
Kaspersky official blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
Tor Project blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tenable Blog
Google Online Security Blog
Google Online Security Blog
PCI Perspectives
PCI Perspectives

博客园 - 唯一浩哥

2026 年,macbook air 2015 升级硬盘注意事项 Java集合系列-HashSet Java基础系列-单例的7种写法 Java基础系列-Lambda Java基础系列-时间日期API Java基础系列-RandomAccess Java基础系列-Optional 那些字段适不适合建索引? Mysql笔记 Spring知识点 Java基础系列-浅拷贝和深拷贝 前端-javascript知识点 前端-jquery知识点 Java基础系列-substring的原理 - 唯一浩哥 - 博客园 Java设计模式之《抽象工厂模式》及使用场景 - 唯一浩哥 - 博客园 Java基础系列-Enum深入解析 - 唯一浩哥 - 博客园 Java基础系列-Collector和Collectors - 唯一浩哥 - 博客园 Java基础系列-Stream - 唯一浩哥 - 博客园 Java并发编程系列-AbstractQueuedSynchronizer - 唯一浩哥 - 博客园
Java基础系列-SPI你认识吗
唯一浩哥 · 2020-10-13 · via 博客园 - 唯一浩哥

原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755313.html

一、SPI是什么

SPI是相对API而言的。

API指的是应用对服务调用方提供的接口,用于提供某种服务、功能,面向的是服务调用方。

SPI指的是应用对服务实现方提供的接口,用于实现某种服务、功能,面向的是服务实现方

二、SPI的使用

2.1 第一步:创建服务接口

package com.dh.spi;

public interface Fruit {
    String getName();
}

2.2 第二步:创建多个服务实现

package com.dh.spi;

public class Apple implements Fruit {
    @Override
    public String getName() {
        return "apple";
    }
}
package com.dh.spi;

public class Banana implements Fruit {
    @Override
    public String getName() {
        return "Banana";
    }
}

这里的两个服务实现类,针对的是两个服务实现方,一方实现了Apple,另一方实现了Banana。

2.3 第三步:创建配置文件

在resource下创建/META-INF/services目录,在services目录下创建以服务接口全限定名为名称的文件:com.dh.spi.Fruit

文件内容为,当前服务实现的服务实现者类的全限定名

com.dh.spi.Apple

2.4 第四步:创建测试类

public class Test {
    public static void main(String[] args) {
        ServiceLoader<Fruit> s = ServiceLoader.load(Fruit.class);
        Iterator<Fruit> it = s.iterator();
        while(it.hasNext())
            System.out.println(it.next().getName());
    }
}

执行结果为:

apple

三、SPI的实现原理

SPI的实现主要依靠的就是ServiceLoader类。使用该类加载接口类型(例如:Fruit.class)

ServiceLoader<Fruit> s = ServiceLoader.load(Fruit.class);

虽然是一个load方法,但是并没有加载到指定的服务实现类,这里仅仅是对加载服务实现类做一些准备工作:

  • 创建ServiceLoader
  • 为service赋值
  • 为loader赋值
  • 为acc赋值
  • 清空providers缓存
  • 为lookupIterator赋值,其实就是创建一个LazyIterator延迟迭代器。

然后创建迭代器:

Iterator<Fruit> it = s.iterator();

iterator方法中采用了匿名内部类的方式定义了一个新的迭代器,这个迭代器中每一个方法都是通过调用之前创建好的延迟迭代器lookupIterator来完成的

最后就是进行迭代加载了。

while(it.hasNext())
    System.out.println(it.next().getName());

hasNext方法调用了延迟迭代器的hasNext方法,内部调用了hasNextService方法,在这个方法中就会设法去找到指定名称(META-INF/services/+接口全限定名)的资源文件。并完成读取文件内容的操作。

然后执行it.next()操作,这个又会调用延迟迭代器的对应方法hasNext,内部调用了nextService方法,这个方法主要功能就是加载上面从文件中读取到的全限定名称表示的类。并生成实例,将实例保存到providers中。

private LinkedHashMap<String,S> providers = new LinkedHashMap<>();