本文目录导读:

- 项目结构
- 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的基本用法,包括接口定义、多个实现、服务加载和实际调用。