Java SPI实现案例

wen java案例 1

本文目录导读:

Java SPI实现案例

  1. 项目结构
  2. SPI接口定义 (spi-api)
  3. 实现A (spi-provider-a)
  4. 实现B (spi-provider-b)
  5. 消费者/调用方 (spi-consumer)
  6. Maven配置 (pom.xml)
  7. 运行结果
  8. 核心要点
  9. 最佳实践

我将为您展示一个完整的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

核心要点

  1. SPI文件位置: META-INF/services/ 目录下
  2. 文件名: 必须是接口的全限定名
  3. : 实现类的全限定名,每行一个
  4. 加载机制: 使用 ServiceLoader.load(接口.class) 加载
  5. 优点: 解耦、可扩展、动态发现实现
  6. 应用场景: JDBC驱动、日志框架等

最佳实践

// 使用线程上下文类加载器(适用于复杂环境)
ServiceLoader<MessageService> services = ServiceLoader.load(
    MessageService.class, 
    Thread.currentThread().getContextClassLoader()
);

这个案例展示了Java SPI的基本用法,包括接口定义、多个实现、服务加载和实际调用。

上一篇JCL案例

下一篇Java日志桥接案例

抱歉,评论功能暂时关闭!