SPI(服务提供发现机制)简单使用

2 篇文章 0 订阅

一、概述

SPI的英文全称是Service Provider Interface,是Java内置的一种服务提供发现机制。一般常用于一些框架或组件库的开发,我们最熟悉JDBC就应用到了SPI机制,并且在Spring、Dubbo中也大量应用了SPI机制。SPI机制是针对同一个接口采用不同的实现,提供给不同的用户使用,目的就是为了提高框架或者组件库的扩展性。

二、Java中SPI的实现

代码示例:
  • 创建ide开发工具的插件接口
package com.ideax.spi.jdk;

/**
 * <p>
 * Idea插件接口
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public interface IdePluginSpi {
    void plugin();
}
  • 为Goland开发插件
package com.ideax.spi.jdk;

/**
 * <p>
 * Goland插件实现类
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class GolandPlugin implements IdePluginSpi {
    @Override
    public void plugin() {
        System.out.println("goland plugin");
    }
}
  • 为Webstorm开发插件
package com.ideax.spi.jdk;

/**
 * <p>
 * Webstorm插件实现类
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class WebstormPlugin implements IdePluginSpi {
    @Override
    public void plugin() {
        System.out.println("webstorm plugin");
    }
}
  • 以Springboot工程为例,在项目的resources目录下新建META-INF/services/目录,在该目录下创建com.ideax.spi.jdk.IdePluginSpi文件,文件中按照如下格式,写入接口的多个实现类全路径
com.ideax.spi.jdk.WebstormPlugin
com.ideax.spi.jdk.GolandPlugin
  • 测试代码
package com.ideax.spi.jdk;

import java.util.ServiceLoader;

/**
 * <p>
 * jdk中的spi测试
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class JdkSpiTest {
    public static void main(String[] args) {
        ServiceLoader<IdePluginSpi> serviceLoader = ServiceLoader.load(IdePluginSpi.class);
        serviceLoader.forEach(IdePluginSpi::plugin);
    }
}
  • 运行结果
    在这里插入图片描述
    说明: Java内置的SPI实现时,要通过java.util.ServiceLoader类,解析项目以及jar文件classPath下META-INF/services/目录中,且以接口全路径命名的文件,同时加载该文件中指定的该接口的实现类,通过这种方式,实现对指定接口实现类的调用。

三、Spring中SPI的实现

代码示例:
  • 创建一个数据库链接接口
package com.ideax.spi.spring;

/**
 * <p>
 * 数据库链接接口
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public interface DatabaseSpi {
    void getConnection();
}
  • PostgreSql实现类
package com.ideax.spi.spring;

/**
 * <p>
 * PostgreSql实现类
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class PostgreSqlDatabase implements DatabaseSpi {
    @Override
    public void getConnection() {
        System.out.println("this database is PostgreSql");
    }
}
  • MySql实现类
package com.ideax.spi.spring;

/**
 * <p>
 * MySql实现类
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class MySqlDatabase implements DatabaseSpi {
    @Override
    public void getConnection() {
        System.out.println("this database is MySql");
    }
}
  • 创建一个缓存链接接口
package com.ideax.spi.spring;

/**
 * <p>
 * 缓存链接接口
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public interface CacheSpi {
    void getConnection();
}
  • Memecache实现类
package com.ideax.spi.spring;

/**
 * <p>
 * Memecache实现类
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class MemecacheCache implements CacheSpi {
    @Override
    public void getConnection() {
        System.out.println("this cache is memecache");
    }
}
  • Redis实现类
package com.ideax.spi.spring;

/**
 * <p>
 * redis实现类
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class RedisCache implements CacheSpi {
    @Override
    public void getConnection() {
        System.out.println("this cache is redis");
    }
}
  • 以Springboot工程为例,在项目的resources目录下新建META-INF目录,在该目录下创建spring.factories文件,文件中按照如下格式,写入接口的多个实现类全路径
com.ideax.spi.spring.DatabaseSpi=\
com.ideax.spi.spring.MySqlDatabase,\
com.ideax.spi.spring.PostgreSqlDatabase

com.ideax.spi.spring.CacheSpi=\
com.ideax.spi.spring.RedisCache,\
com.ideax.spi.spring.MemecacheCache
  • 测试代码
package com.ideax.spi.spring;

import org.springframework.core.io.support.SpringFactoriesLoader;

import java.util.List;

/**
 * <p>
 * Spring中的Spi测试
 * </p>
 *
 * @author zhangxs
 * @since 2023-02-16
 */
public class SpringSpiTest {
    public static void main(String[] args) {
        List<DatabaseSpi> databaseSpis = SpringFactoriesLoader
                .loadFactories(DatabaseSpi.class, Thread.currentThread().getContextClassLoader());
        databaseSpis.forEach(DatabaseSpi::getConnection);

        List<CacheSpi> cacheSpis = SpringFactoriesLoader
                .loadFactories(CacheSpi.class, Thread.currentThread().getContextClassLoader());
        cacheSpis.forEach(CacheSpi::getConnection);
    }
}
  • 运行结果
    在这里插入图片描述
    说明: Spring中的SPI,类似于Java中SPI的设计理念,并且在其基础上进行了扩展,服务提供接口不再要求必须放到META-INF/services/目录下,Spring中是通过配置spring.factories方式实现SPI机制的。通过该机制,我们也可以在不修改Spring源码的前提下,根据我们的需求,扩展Spring框架。

四、总结

两种实现SPI的方式非常类似,我们可以根据需求自行选择,二者最明显的区别在于:

  • Java中的SPI,每一个服务提供接口只能对应一个配置文件,配置文件中存放当前接口的所有实现类全路径,多个服务提供接口就必须对应多个配置文件,所有配置都必须放在META-INF/services/目录下。
  • Spring中的SPI,只需要spring.factories一个配置文件,其中通过key-value的方式配置多个接口及其实现类,以接口全路径作为key,实现类全路径作为value,多个实现类直接用英文逗号隔开即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值