java中SPI服务提供者的接口使用总结

目录
  • 一:什么是SPI
  • 二:java SPI示例
    • 1.SPI服务提供方
    • 2.SPI服务应用方开发者
  • 三:JavaSPI 机制的核心-ServiceLoader

    一:什么是SPI

    SPI:“服务提供者的接口”,是一种服务发现机制

    用于实现框架或库的扩展点,允许在运行时动态地插入或更换组件实现。

    它提供了一个框架(JDK1.6后ServiceLoader)来发现和加载服务实现,使得软件模块能够灵活地选择和使用不同的服务提供商。

    在java中通俗讲就是:

    • 对框架或第三方jar包提供者来说可制定规范,提供给开发者可扩展性
    • 对开发者来说可以根据需要轻松替换框架或第三方jar包中提供了SPI机制的接口的实现

    在AJ-Captcha与Nacos中均提供了SPI扩展。

    二:java SPI示例

    1.SPI服务提供方

    SPI服务提供方架构图:

    定义接口规范:

    public interface SpiService {
        /**
         * 呼叫方式
         */
        void call();
    }
    

    加载具体的服务实现:

    package com.lmy.config;
    
    import com.lmy.service.SpiService;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.ServiceLoader;
    
    /**
     * @author : lmy
     * @date : 2025/9/14 上午 11:35
     * 加载具体的服务实现
     */
    public class SpiServiceLoader {
        private static volatile SpiServiceLoader LOADER;
    
        private final SpiService spiService;
    
        private final List<SpiService> spiServiceList;
    
        /**
         * 加载服务
         * */
        private SpiServiceLoader() {
            ServiceLoader<SpiService> loader = ServiceLoader.load(SpiService.class);
            List<SpiService> list = new ArrayList<>();
            for (SpiService spiService : loader) {
                list.add(spiService);
            }
            spiServiceList = list;
            if (!list.isEmpty()) {
                // 取第一个
                spiService = list.get(0);
            } else {
                spiService = null;
            }
        }
    
        /**
         * SpiServiceLoader 单例加载
         * */
        public static SpiServiceLoader getLOADER() {
            if (LOADER == null) {
                synchronized (SpiServiceLoader.class) {
                    if (LOADER == null) {
                        LOADER = new SpiServiceLoader();
                    }
                }
            }
            return LOADER;
        }
    
    
        public void call(){
            if(spiServiceList.isEmpty()){
                System.out.println("SpiService服务未加载!");
            }else {
                SpiService spiService = spiServiceList.get(0);
                spiService.call();
            }
        }
    
    }
    
    

    默认实现:

    package com.lmy.service.impl;
    
    import com.lmy.service.SpiService;
    
    /**
     * @author : lmy
     * @date : 2025/9/14 上午 10:58
     * 默认实现
     */
    public class SpiServiceImpl implements SpiService {
        @Override
        public void call() {
            System.out.println("默认手机呼叫");
        }
    }
    
    

    指定服务实现方式:
    须在resource下创建META-INF.services,文件名为接口全限定类名,配置为需要被加载的接口实现类的全限定类名

    com.lmy.service.impl.SpiServiceImpl
    

    项目打包发布本地:

    2.SPI服务应用方开发者

    开发者引入jar包使用服务:

    <dependency>
        <groupId>com.lmy</groupId>
        <artifactId>SPI-interface</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    
    package com.lmy.Spi;
    
    import com.lmy.config.SpiServiceLoader;
    import org.junit.Test;
    
    /**
     * @author : lmy
     * @date : 2025/9/14 上午 11:48
     */
    public class SpiTest {
    
        @Test
        public void spiTest () {
            SpiServiceLoader loader = SpiServiceLoader.getLOADER();
            loader.call();
        }
    }
    
    

    执行结果:

    开发者根据需要扩展替换为自己的服务实现:

    package com.lmy.Spi.service;
    
    import com.lmy.service.SpiService;
    
    /**
     * @author : lmy
     * @date : 2025/9/14 下午 2:09
     */
    public class SpiServiceNewImpl implements SpiService {
        @Override
        public void call() {
            System.out.println("卫星直呼");
        }
    }
    
    

    com.lmy.Spi.service.SpiServiceNewImpl
    

    执行结果:

    三:JavaSPI 机制的核心-ServiceLoader

    上面代码可见是通过ServiceLoader 去加载具体的服务实现的

    ServiceLoader 是从JDK1.6 开始提供的一个类,用于加载服务提供者。

    进入源码可见:
    其中 String PREFIX = “META-INF/services/”;
    这个就是JDK的SPI功能规定的具体服务实现的配置信息文件所在的目录 META-INF/services/

    JDK的SPI规定 服务实现者需要在 META-INF/services/ 目录下 新建文件名为 SPI接口全限定类名的文件
    文件内容为 服务实现者需要被加载的具体类的全限定类名

    本文转自网络,如有侵权请联系客服删除。