Java SPI:Service Provider Interface

SPI機制簡介

????????SPI(Service Provider Interface),是從JDK6開始引入的,一種基于ClassLoader來發現并加載服務的機制。

? ? ? ? 一個標準的SPI,由3個組件構成,分別是:

  • Service:是一個公開的接口或者抽象類,定義了一個抽象的功能模塊;
  • Service Provider:是Service接口的實現子類;
  • ServiceLoader:是SPI機制的核心組件,負責在運行時發現并加載Service Provider。

SPI運行流程

? ? ? ? SPI運行流程如下圖所示,

ServiceLoader類

? ? ? ? ServiceLoader是SPI機制的核心組件,負責在運行時發現并加載Service Provider。該類提供了load方法,用于在程序運行過程中去加載第三方提供的Service接口實現類,得到接口實例;后續過程中,只需要通過接口實例去執行對應的操作即可。

? ? ? ? 假設,我們有這樣一個InternetService?接口,用來提供網絡連接服務。

/*** 網絡連接服務接口SPI-Service*/
public interface InternetService {void connectInternet();
}

? ? ? ? 然后為其提供接口實現子類,

package cn.mobile;import spi.InternetService;public class BeijingChinaMobileMobile implements InternetService {@Overridepublic void connectInternet() {System.out.println("connect internet By [Beijing China Mobile]");}
}

? ? ? ?這樣寫在單體項目中自然是可以的,但是,如果我們要讓別人也能在項目中使用這個接口提供的網絡連接服務,就有點難受了。好在SPI機制就是用來做服務發現和加載工作的,我們可以將其改造成符合SPI標準的一套通用工具。

service服務定義

? ? ? ? 接口就是在定義標準,而這個標準需要交由第三方進行實現。

1. 創建maven項目,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>simple_spi_example</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>simple-api</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties></project>

?2.定義接口標準,

package spi;/*** 網絡連接服務接口SPI-Service*/
public interface InternetService {void connectInternet();
}

service provider服務的第三方實現

? ? ? ? service provider是Service接口的實現子類。以下我們提供兩個第三方實現,分別命名為A、B。

第三方A實現

1. 創建Maven項目,引入接口標準依賴,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>simple_spi_example</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>simple-spi-mobile</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><artifactId>simple-api</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>

2. 實現SPI接口,

package cn.mobile;import spi.InternetService;public class ChinaMobile implements InternetService {@Overridepublic void connectInternet() {System.out.println("connect internet By [China Mobile]");}
}
package cn.mobile;import spi.InternetService;public class BeijingChinaMobileMobile implements InternetService {@Overridepublic void connectInternet() {System.out.println("connect internet By [Beijing China Mobile]");}
}

3.提供service元數據,

? ? ? ? 在resources目錄下,新建資源文件META-INF\services\spi.InternetService,文件名:spi.InternetService(父接口的全路徑名稱),

cn.mobile.ChinaMobile
cn.mobile.BeijingChinaMobileMobile 

第三方B實現

1. 創建Maven項目,引入接口標準依賴,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>simple_spi_example</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>simple-spi-unicom</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><artifactId>simple-api</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></dependency></dependencies>
</project>

2. 實現SPI接口,

package cn.unicom;import spi.InternetService;public class ChinaUniCom implements InternetService {@Overridepublic void connectInternet() {System.out.println("connect internet By [ChinaUniCom]");}
}

3.提供service元數據,

? ? ? ? 在resources目錄下,新建資源文件META-INF\services\spi.InternetService,文件名:spi.InternetService(父接口的全路徑名稱),

cn.unicom.ChinaUniCom

ServiceLoader服務發現和服務加載

?1. 在主項目中引入service服務的第三方實現相關依賴,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>simple_spi_example</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>smaple-company</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><artifactId>simple-api</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.example</groupId><artifactId>simple-spi-unicom</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.example</groupId><artifactId>simple-spi-mobile</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>

2. 通過ServiceLoader發現并加載服務

package com.company;import spi.InternetService;import java.util.ServiceLoader;public class Application {public static void main(String[] args) {ServiceLoader<InternetService> load = ServiceLoader.load(InternetService.class);for (InternetService provider : load) {provider.connectInternet();}}
}

3.執行結果如下

ServiceLoader源碼分析

?

? ? ? ? 分析,

// ServiceLoader實現了Iterable接口,可以遍歷所有的服務實現者
public final class ServiceLoader<S> implements Iterable<S>
{// 查找配置文件的目錄private static final String PREFIX = "META-INF/services/";// 表示要被加載的服務的類或接口private final Class<S> service;// 這個ClassLoader用來定位,加載,實例化服務提供者private final ClassLoader loader;// 訪問控制上下文private final AccessControlContext acc;// 緩存已經被實例化的服務提供者,按照實例化的順序存儲private LinkedHashMap<String,S> providers = new LinkedHashMap<>();// 迭代器private LazyIterator lookupIterator; 
}
// 服務提供者查找的迭代器
public Iterator<S> iterator() {return new Iterator<S>() {Iterator<Map.Entry<String,S>> knownProviders= providers.entrySet().iterator();// hasNext方法public boolean hasNext() {if (knownProviders.hasNext())return true;return lookupIterator.hasNext();}// next方法public S next() {if (knownProviders.hasNext())return knownProviders.next().getValue();return lookupIterator.next();}};
}
// 服務提供者查找的迭代器
private class LazyIterator implements Iterator<S> {// 服務提供者接口Class<S> service;// 類加載器ClassLoader loader;// 保存實現類的urlEnumeration<URL> configs = null;// 保存實現類的全名Iterator<String> pending = null;// 迭代器中下一個實現類的全名String nextName = null;public boolean hasNext() {if (nextName != null) {return true;}if (configs == null) {try {String fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);elseconfigs = 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;}public S next() {if (!hasNext()) {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, x);}throw new Error();          // This cannot happen}
}

SPI應用場景舉例

在JDBC4.0之前,連接數據庫的時候,通常會用Class.forName("com.mysql.jdbc.Driver")這句先加載數據庫相關的驅動,然后再進行獲取連接等的操作。而JDBC4.0之后不需要Class.forName來加載驅動,直接獲取連接即可,這里使用了Java的SPI擴展機制來實現。

? ? ? ? 注冊數據庫連接驅動就是一個典型的例子,以PostGreSQL數據庫連接驅動為例,我們知道:java中定義了接口java.sql.Driver,但是并沒有提供具體的實現,具體的實現都是由不同廠商來提供的,所以我們實際開發時,需要先去找到對應的數據庫連接驅動,把驅動加載到應用中,然后才能去執行數據庫的種種操作。

? ? ? ? 查看postgresql依賴jar包,會發現在META-INFO下的services路徑下,也提供了java.sql.Driver驅動類的實現子類信息,

? ? ? ? 文件內容如下,

org.postgresql.Driver

? ? ? ? ?這樣,就可以基于SPI機制,動態加載第三方提供的Driver數據庫連接驅動,實現數據庫相關的操作。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/710715.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/710715.shtml
英文地址,請注明出處:http://en.pswp.cn/news/710715.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java ElasticSearch面試題

Java ElasticSearch面試題 前言1、ElasticSearch是什么&#xff1f;2. 說說你們公司ES的集群架構&#xff0c;索引數據大小&#xff0c;分片有多少 &#xff1f;3. ES的倒排索引是什么&#xff1f;4. ES是如何實現 master 選舉的?5. 描述一下 ES索引文檔的過程&#xff1a;6、…

Centos系統(Linux)掛載硬盤/數據盤詳細操作和開機自動掛載的兩種方式

前提&#xff1a;已經做好磁盤陣列&#xff0c;將磁盤劃分好 磁盤初始化操作步驟&#xff08;如果已經可以正常掛載可跳過)&#xff1a; 使用fdisk -l命令查看多出來的大容量的磁盤名稱&#xff08;如果多塊磁盤&#xff0c;查看需要掛載的磁盤名稱&#xff09;&#xff0c;一…

embedding的原理和結構

embedding(向量化)是一個將數據轉化為向量矩陣的過程&#xff0c;作用是&#xff1a;將高維稀疏向量轉化為稠密向量&#xff0c;從而方便下游模型處理 簡單的概念大家應該都知道了&#xff0c;以LLM為例 輸入&#xff1a;文字 模型&#xff1a;embedding 輸出&#xff1a;向量…

c++高精度乘法的原理及c++代碼講解

高精度乘法的原理主要是利用數學中乘法的基本原理&#xff0c;將大整數拆分成各個位數的相乘&#xff0c;然后累加得到最終結果。其過程如下&#xff1a; 將兩個大整數相乘&#xff0c;從低位開始逐位相乘&#xff0c;得到部分乘積&#xff1b;將每一位的部分乘積相加&#xf…

【Emgu CV教程】7.8、圖像銳化(增強)之同態濾波

文章目錄 一、同態濾波大體原理二、代碼三、效果舉例 一、同態濾波大體原理 之前介紹的幾個銳化、增強方法&#xff0c;包括更早之前介紹的圖像模糊方法&#xff0c;都是基于空間域進行處理&#xff0c;也就是直接對目標點周邊像素值進行各種數學運算。而這篇文章提到的同態濾…

學習計算機的好處

之前寫了那么多計算機知識&#xff0c;卻沒有一篇寫我學計算機的初衷。 掌握計算機技術不僅可以提高我們的就業能力和競爭力&#xff0c;同時有助于我們更好地認識世界&#xff0c;提高工作效率和解決問題的能力&#xff0c;更好地利用科技創造更美好的未來。 因此&#xff0c…

pyvisa庫實現儀器控制

python控制儀器實現自動化常用pyvisa庫&#xff0c;基本控制可大致分為創建儀器控制對象、寫入控制指令、讀取儀表信息和查詢儀表狀態&#xff0c;下面進行基本的講解。 pyvisa庫創建儀表控制對象 import tkinter.messagebox import pyvisaclass InstrumentControl:inst Non…

喜迎喬遷,開啟新章 ▏易我科技新辦公區喬遷慶典隆重舉行

2024年1月18日&#xff0c;易我科技新辦公區喬遷慶典在熱烈而喜慶的氛圍中隆重舉行。新辦公區的投入使用&#xff0c;標志著易我科技將以嶄新姿態邁向新的發展階段。 ▲ 易我科技新辦公區 隨著公司業務的不斷發展和壯大&#xff0c;為了更好地適應公司發展的需要&#xff0c;…

2024-02-29(Flink)

1.Flink原理&#xff08;角色分工&#xff09; 2.Flink執行流程 on yarn版&#xff1a; 3.相關概念 1&#xff09;DataFlow&#xff1a;Flink程序在執行的時候會被映射成一個數據流模型&#xff1b; 2&#xff09;Operator&#xff1a;數據流模型中的每一個操作被稱作Operat…

Spring Boot 高級實踐探索:深度解讀與實戰演練

隨著開發者對Spring Boot框架的基礎運用日漸嫻熟&#xff0c;邁向更深層次的技術探究和應用場景拓展顯得尤為重要。本文將帶領讀者深入研究Spring Boot的若干核心進階特性&#xff0c;并結合實際項目案例&#xff0c;涵蓋自動化測試策略的深化應用、高級配置管理機制的巧妙運用…

Redis 之四:Redis 事務和樂觀鎖

事務特點 Redis 事務可以一次執行多個命令&#xff0c; 并且帶有以下三個重要的保證&#xff1a; 批量操作在發送 EXEC 命令前被放入隊列緩存。 收到 EXEC 命令后進入事務執行&#xff0c;事務中任意命令執行失敗&#xff0c;其余的命令依然被執行。不具備原子性。 在事務執…

通訊錄——C語言實現

頭文件Contact.h #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<stdlib.h> #pragma once #define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 30//表示一個人的信息 //struct…

npm使用國內淘寶鏡像的方法整理

命令配置安裝&#xff1a; 淘寶鏡像&#xff1a; npm config set registry https://registry.npm.taobao.org/ 官方鏡像&#xff1a; npm config set registry https://registry.npmjs.org 通過cnpm安裝&#xff1a; npm install -g cnpm --registryhttps://registry.npm.…

PTA L2-003 月餅 (附坑點說明)

月餅是中國人在中秋佳節時吃的一種傳統食品&#xff0c;不同地區有許多不同風味的月餅。現給定所有種類月餅的庫存量、總售價、以及市場的最大需求量&#xff0c;請你計算可以獲得的最大收益是多少。 注意&#xff1a;銷售時允許取出一部分庫存。樣例給出的情形是這樣的&#…

如何在Java中反轉字符串?

目錄 1. 使用StringBuilder的reverse方法&#xff1a; 2. 使用for循環和字符數組&#xff1a; 3. 使用遞歸&#xff1a; 4. 使用Java 8的Stream API&#xff1a; More Java中&#xff0c;反轉字符串可以通過多種方式實現。以下是一些常見的方法&#xff1a; 1. Java中使用…

【Python】PyGameUI控件

哈里前段時間寫了一個windows平板上自娛自樂&#xff08;春節和家人一起玩&#xff09;基于pygame的大富翁游戲。 pygame沒有按鈕之類的UI控件&#xff0c;寫起來不怎么順手。就自己寫一個簡單的框架。 倉庫地址 哈里PygameUi: pygame ui封裝自用 (gitee.com) 使用示例 示…

上海亞商投顧:滬指終結月線6連陰 北向資金凈買入超160億

上海亞商投顧前言&#xff1a;無懼大盤漲跌&#xff0c;解密龍虎榜資金&#xff0c;跟蹤一線游資和機構資金動向&#xff0c;識別短期熱點和強勢個股。 一.市場情緒 三大指數昨日低開高走&#xff0c;滬指重新站上3000點&#xff0c;深成指、創業板指大漲超3%。半導體產業鏈全…

實時聊天系統PHP

實時聊天系統可以讓用戶在網站上實時交流&#xff0c;這對社交平臺、在線客服等網站非常有幫助。以下是一個簡單的基于 PHP 和 WebSocket 的實時聊天系統示例&#xff1a; 1. 首先創建一個 HTML 文件 index.html 來顯示聊天界面和發送消息的表單&#xff1a; html <!DOCTYP…

【C#】 List.Sort 方法

【C#】 List.Sort 方法 在C#中&#xff0c;List.Sort()不僅為系統自帶的變量(int, float, double …)類型的集合提供默認排序&#xff0c;還提供了自定義的排序方法。 List自帶排序 List<int> list new List<int>(); list.Add(5); list.Add(3); list.Add(4); l…

探索那些能喚起情感共鳴的壁紙

1、方小童在線工具集 網址&#xff1a; 方小童 該網站是一款在線工具集合的網站&#xff0c;目前包含PDF文件在線轉換、隨機生成美女圖片、精美壁紙、電子書搜索等功能&#xff0c;喜歡的可以趕緊去試試&#xff01;