點一下關注吧!!!非常感謝!!持續更新!!!
🚀 AI篇持續更新中!(長期更新)
AI煉丹日志-30-新發布【1T 萬億】參數量大模型!Kimi?K2開源大模型解讀與實踐,持續打造實用AI工具指南!📐🤖
💻 Java篇正式開啟!(300篇)
目前2025年07月16日更新到:
Java-74 深入淺出 RPC Dubbo Admin可視化管理 安裝使用 源碼編譯、Docker啟動
MyBatis 已完結,Spring 已完結,Nginx已完結,Tomcat已完結,分布式服務正在更新!深入淺出助你打牢基礎!
📊 大數據板塊已完成多項干貨更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余項核心組件,覆蓋離線+實時數倉全棧!
大數據-278 Spark MLib - 基礎介紹 機器學習算法 梯度提升樹 GBDT案例 詳解
Java SPI(Service Provider Interface)是一種內置的服務發現機制,結合接口編程、策略模式和配置文件,允許在運行時動態加載實現類。SPI廣泛用于 JDBC、JNDI、日志、XML 解析等場景,核心依賴 ServiceLoader 實現解耦與可插拔擴展。使用時需在 META-INF/services 下配置實現類路徑。Dubbo 基于 SPI 機制實現了自己的擴展體系,支持負載均衡、協議選擇等插件化設計,用戶可自定義實現并通過注解 @SPI 與配置文件指定默認服務,實現靈活的服務擴展與適配。
SPI (Service Provider Interface)
SPI 簡介
SPI(Service Provider Interface)是 Java 開發工具包(JDK)內置的一種服務提供發現機制。它作為一種標準化的服務擴展點發現方式,目前被廣泛應用于各種框架中來實現服務的動態擴展。
SPI 工作機制
SPI 的核心思想是"面向接口編程+策略模式+配置文件"的組合實現。具體工作流程為:
- 定義服務接口(如
java.sql.Driver
) - 在
META-INF/services/
目錄下創建以接口全限定名命名的文件 - 在該文件中寫入具體實現類的全限定名(如
com.mysql.jdbc.Driver
) - 通過
ServiceLoader
類加載并實例化這些實現類
SPI 的優勢
- 解耦性:將服務接口與具體實現分離,調用方只需面向接口編程
- 擴展性:新增服務實現無需修改原有代碼,只需添加新的實現類
- 動態性:服務實現可以在運行時動態發現和加載
- 標準化:作為 JDK 標準機制,提供統一的擴展方式
典型應用場景
- JDBC 數據庫驅動加載(
java.sql.Driver
) - JNDI 服務提供者(
javax.naming.spi.InitialContextFactory
) - XML 解析器(
javax.xml.parsers.DocumentBuilderFactory
) - 日志框架(
java.util.logging.LogManager
) - Spring Boot 自動配置機制
示例:JDBC 通過 SPI 加載不同數據庫驅動
// 通過 SPI 自動發現并加載驅動
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, user, password);
SPI 機制實現了"約定優于配置"的原則,是 Java 生態中實現可插拔架構的重要基礎。
JDK中的SPI
Java SPI 使用詳解
1. 標準服務接口定義
首先需要定義一個標準服務接口,作為SPI機制的契約。這個接口將被服務提供者實現,被服務調用者使用。
示例:
// 定義支付接口標準
package com.example.spi;public interface PaymentService {/*** 支付方法* @param amount 支付金額* @return 支付結果*/String pay(double amount);/*** 獲取支付方式名稱* @return 支付方式*/String getPaymentMethod();
}
2. 服務提供者實現
2.1 實現接口
服務提供者需要提供該接口的具體實現。例如我們實現一個支付寶支付服務:
package com.example.provider;import com.example.spi.PaymentService;public class AlipayService implements PaymentService {@Overridepublic String pay(double amount) {return "支付寶支付成功,金額:" + amount;}@Overridepublic String getPaymentMethod() {return "Alipay";}
}
2.2 創建配置文件
在資源目錄下創建 META-INF/services
目錄,并添加以接口全限定名命名的文件:
文件路徑:META-INF/services/com.example.spi.PaymentService
文件內容:
com.example.provider.AlipayService
2.3 打包注意事項
- 實現類必須包含無參構造器
- 實現類所在的JAR包需要包含META-INF/services目錄
- 多個實現可以在配置文件中逐行列出
3. 服務調用者使用
3.1 通過ServiceLoader加載
調用者使用ServiceLoader動態加載實現:
import com.example.spi.PaymentService;
import java.util.ServiceLoader;public class PaymentApp {public static void main(String[] args) {ServiceLoader<PaymentService> services = ServiceLoader.load(PaymentService.class);for (PaymentService service : services) {System.out.println("發現支付服務: " + service.getPaymentMethod());System.out.println(service.pay(100.0));}}
}
3.2 類路徑配置
確保:
- 接口定義的JAR包在classpath中
- 所有實現類的JAR包也在classpath中
- 如果使用Maven/Gradle,添加相應的依賴關系
4. 高級用法
4.1 多個實現共存
可以同時提供多個實現,例如再添加一個微信支付:
package com.example.provider;import com.example.spi.PaymentService;public class WechatPayService implements PaymentService {@Overridepublic String pay(double amount) {return "微信支付成功,金額:" + amount;}@Overridepublic String getPaymentMethod() {return "WeChat Pay";}
}
然后在配置文件中添加:
com.example.provider.AlipayService
com.example.provider.WechatPayService
4.2 實際應用場景
SPI機制常用于:
- 支付網關集成
- 數據庫驅動加載(如JDBC)
- 日志框架適配
- 序列化協議支持
- 插件化系統開發
5. 注意事項
- ServiceLoader不是線程安全的
- 實現類必須有無參構造器
- 服務加載是延遲初始化的
- 每次調用ServiceLoader.load()都會返回新的實例
- 可以通過迭代器模式獲取所有實現
Dubbo中的SPI
dubbo 中大量的使用了SPI來作為擴展點,通過實現同一接口的前提下,可以進行定制自己的實現類,比如比較常見的協議,負載均衡,都可以通過 SPI 的方式進行定制化,自己擴展。Dubbo 中已經存在的所有已經實現的擴展點。
下圖中是默認的提供的負載均衡的策略:
擴展點使用
我們將使用三個項目來演示 Dubbo 中擴展點的使用方式,一個主項目 main,一個服務接口項目 api,一個服務實現項目 impl
API 項目創建
● 導入坐標 dubbo
● 創建接口
● 在接口上使用 SPI
impl 項目創建
● 導入 api 項目的依賴
● 建立實現類,為了表達支持多個實現的目的,這里分別創建兩個實現,分別是:WzkHumanHelloService 和 WzkDogHelloService。
● SPI 進行聲明操作,在 resources 目錄下創建 META-INF/dubbo 目錄,在目錄下創建 “icu.wzk.service.WzkHelloService” 的文件
該文件內容中寫入進行擴展的類:
human=icu.wzk.service.impl.WzkHumanHelloService
dog=icu.wzk.service.impl.WzkDogHelloService
我們可以看到下面的目錄如下:
這里我們需要回顧一下之前的內容,之前我們已經定義了 WzkHelloService,我們在之前代碼的基礎上,進行了如下的擴展,這里值得注意的是:默認實現 這里選的是 dog, 也就是:icu.wzk.service.impl.WzkDogHelloService。
PS:SPI指定一個默認實現,屬于一個兜底機制,比如消費者調用的時候,沒有指定服務,那就會走這個默認的服務。
package icu.wzk.service;import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;@SPI("dog")
public interface WzkHelloService {String sayHello(String name);@AdaptiveString sayHello( URL url);
}
接著我們要在目錄下新建這兩個類出來:
WzkHumanHelloService的內容如下所示:
package icu.wzk.service.impl;import icu.wzk.service.WzkHelloService;
import org.apache.dubbo.common.URL;public class WzkHumanHelloService implements WzkHelloService {@Overridepublic String sayHello(String name) {return "WzkHuman: " + name;}@Overridepublic String sayHello(URL url) {return "WzkHuman URL: " + url;}
}
我們也實現一下 WzkDogHelloService,實現的內容如下:
package icu.wzk.service.impl;import icu.wzk.service.WzkHelloService;
import org.apache.dubbo.common.URL;public class WzkDogHelloService implements WzkHelloService {@Overridepublic String sayHello(String name) {return "WzkDog: " + name;}@Overridepublic String sayHello(URL url) {return "WzkDog URL: " + url;}
}