本文目录导读:

- 项目结构
- SPI接口定义 (spi-api)
- 实现A (spi-provider-a)
- 实现B (spi-provider-b)
- 消费者/调用方 (spi-consumer)
- Maven配置 (pom.xml)
- 运行结果
- 核心要点
- 最佳实践
我将为您展示一个完整的Java SPI(Service Provider Interface)实现案例。
项目结构
spi-demo/
├── spi-api/ # SPI接口定义
├── spi-provider-a/ # 实现A
├── spi-provider-b/ # 实现B
└── spi-consumer/ # 消费者/调用方
SPI接口定义 (spi-api)
// File: spi-api/src/main/java/com/example/spi/MessageService.java
package com.example.spi;
/**
* 消息服务接口
*/
public interface MessageService {
/**
* 发送消息
* @param message 消息内容
* @return 发送结果
*/
String send(String message);
/**
* 服务提供者名称
*/
String getProviderName();
}
// File: spi-api/src/main/java/com/example/spi/MessageServiceFactory.java
package com.example.spi;
import java.util.ServiceLoader;
/**
* SPI 工厂类
*/
public class MessageServiceFactory {
/**
* 获取所有可用的服务实现
*/
public static ServiceLoader<MessageService> getServices() {
return ServiceLoader.load(MessageService.class);
}
/**
* 获取第一个可用的服务实现
*/
public static MessageService getFirstService() {
ServiceLoader<MessageService> services = ServiceLoader.load(MessageService.class);
for (MessageService service : services) {
if (service != null) {
return service;
}
}
throw new RuntimeException("No MessageService implementation found!");
}
/**
* 根据名称获取特定的服务实现
*/
public static MessageService getServiceByName(String providerName) {
ServiceLoader<MessageService> services = ServiceLoader.load(MessageService.class);
for (MessageService service : services) {
if (providerName.equals(service.getProviderName())) {
return service;
}
}
return null;
}
}
实现A (spi-provider-a)
// File: spi-provider-a/src/main/java/com/example/spi/impl/EmailMessageService.java
package com.example.spi.impl;
import com.example.spi.MessageService;
/**
* 邮件消息实现
*/
public class EmailMessageService implements MessageService {
@Override
public String send(String message) {
return "[Email] Sending: " + message;
}
@Override
public String getProviderName() {
return "email";
}
}
// File: spi-provider-a/src/main/resources/META-INF/services/com.example.spi.MessageService // com.example.spi.impl.EmailMessageService
实现B (spi-provider-b)
// File: spi-provider-b/src/main/java/com/example/spi/impl/SmsMessageService.java
package com.example.spi.impl;
import com.example.spi.MessageService;
/**
* 短信消息实现
*/
public class SmsMessageService implements MessageService {
@Override
public String send(String message) {
return "[SMS] Sending: " + message;
}
@Override
public String getProviderName() {
return "sms";
}
}
// File: spi-provider-b/src/main/java/com/example/spi/impl/WechatMessageService.java
package com.example.spi.impl;
import com.example.spi.MessageService;
/**
* 微信消息实现
*/
public class WechatMessageService implements MessageService {
@Override
public String send(String message) {
return "[WeChat] Sending: " + message;
}
@Override
public String getProviderName() {
return "wechat";
}
}
// File: spi-provider-b/src/main/resources/META-INF/services/com.example.spi.MessageService // com.example.spi.impl.SmsMessageService // com.example.spi.impl.WechatMessageService
消费者/调用方 (spi-consumer)
// File: spi-consumer/src/main/java/com/example/consumer/Main.java
package com.example.consumer;
import com.example.spi.MessageService;
import com.example.spi.MessageServiceFactory;
import java.util.ServiceLoader;
public class Main {
public static void main(String[] args) {
System.out.println("=== Java SPI Demo ===\n");
// 方法1:使用ServiceLoader直接遍历
System.out.println("方法1:使用ServiceLoader直接遍历");
ServiceLoader<MessageService> services = ServiceLoader.load(MessageService.class);
for (MessageService service : services) {
System.out.println("Provider: " + service.getProviderName());
System.out.println(service.send("Hello SPI!"));
System.out.println();
}
// 方法2:使用工厂类
System.out.println("\n方法2:使用工厂类获取所有服务");
ServiceLoader<MessageService> allServices = MessageServiceFactory.getServices();
for (MessageService service : allServices) {
System.out.println("Provider: " + service.getProviderName());
System.out.println(service.send("Hello Factory!"));
System.out.println();
}
// 方法3:获取第一个服务
System.out.println("\n方法3:获取第一个服务");
MessageService firstService = MessageServiceFactory.getFirstService();
System.out.println("First Provider: " + firstService.getProviderName());
System.out.println(firstService.send("Hello First!"));
// 方法4:按名称获取特定服务
System.out.println("\n方法4:按名称获取特定服务");
MessageService smsService = MessageServiceFactory.getServiceByName("sms");
if (smsService != null) {
System.out.println("Found SMS Provider");
System.out.println(smsService.send("Hello SMS!"));
}
MessageService invalidService = MessageServiceFactory.getServiceByName("invalid");
System.out.println("Invalid provider: " + (invalidService == null ? "not found" : "found"));
}
}
Maven配置 (pom.xml)
spi-api/pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spi-api</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
</project>
spi-provider-a/pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spi-provider-a</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>spi-api</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
spi-consumer/pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spi-consumer</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>spi-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>spi-provider-a</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>spi-provider-b</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
运行结果
=== Java SPI Demo ===
方法1:使用ServiceLoader直接遍历
Provider: email
[Email] Sending: Hello SPI!
Provider: sms
[SMS] Sending: Hello SPI!
Provider: wechat
[WeChat] Sending: Hello SPI!
方法2:使用工厂类获取所有服务
Provider: email
[Email] Sending: Hello Factory!
Provider: sms
[SMS] Sending: Hello Factory!
Provider: wechat
[WeChat] Sending: Hello Factory!
方法3:获取第一个服务
First Provider: email
[Email] Sending: Hello First!
方法4:按名称获取特定服务
Found SMS Provider
[SMS] Sending: Hello SMS!
Invalid provider: not found
核心要点
- SPI文件位置:
META-INF/services/目录下 - 文件名: 必须是接口的全限定名
- : 实现类的全限定名,每行一个
- 加载机制: 使用
ServiceLoader.load(接口.class)加载 - 优点: 解耦、可扩展、动态发现实现
- 应用场景: JDBC驱动、日志框架等
最佳实践
// 使用线程上下文类加载器(适用于复杂环境)
ServiceLoader<MessageService> services = ServiceLoader.load(
MessageService.class,
Thread.currentThread().getContextClassLoader()
);
这个案例展示了Java SPI的基本用法,包括接口定义、多个实现、服务加载和实际调用。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播