图片

作者 | 梁桂钊

出品 | 服务端思维

一般情况下,我们会通过 API 对外提供服务。这里,API 提供服务的接口的逻辑是固定的,换句话说,它具有通用性。但是,但我们遇到具有类似的业务逻辑的场景时,即核心的主干逻辑相同,而细节的实现略有不同,那我们该何去何从?很多时候,我们会选择提供多个 API 接口给不同的业务方使用。事实上,我们可以通过 SPI 扩展点来实现的更加优雅。什么是 SPI?SPI 的英文全称是 Serivce Provider Interface,即服务提供者接口,它是一种动态发现机制,可以在程序执行的过程中去动态的发现某个扩展点的实现类。因此,当 API 被调用时会动态加载并调用 SPI 的特定实现方法。

此时,你是不是联想到了模版方法模式。模板方法模式的核心思想是定义骨架,转移实现,换句话说,它通过定义一个流程的框架,而将一些步骤的具体实现延迟到子类中。事实上,在微服务的落地过程中,这种思想也给我们提供了非常好的理论基础。

图片

现在,我们来看一个案例:电商业务场景中的未发货仅退款。这种情况在电商业务中非常场景,用户下单付款后由于各种原因可能就申请退款了。此时,因为不涉及退货,所以只需要用户申请退款并填写退款原因,然后让卖家审核退款。那么,由于不同平台的退款原因可能不同,我们可以考虑通过 SPI 扩展点来实现。

图片
我们先来看下 JDK 对 SPI 机制的支持。在面向对象编程的设计中,我们会采取面向接口编程的方式。

  1. public interface IRefundSeason {

  2.    default List<String> invoke(){

  3.        // 1. 前置业务

  4.        // 。。。

  5.        // 2. 获取退款原因列表

  6.        List<String> refundSeasons = getRefundSeasonList();

  7.        // 3. 后置业务

  8.        // 。。。

  9.        return refundSeasons;

  10.    }


  11.    List<String> getRefundSeasonList();

  12. }

这里,我们在简单地定义两个实现类。

  1. public class RefundSeason1 implements IRefundSeason{


  2.    @Override

  3.    public List<String> getRefundSeasonList() {

  4.        return ImmutableList.of("商品降价","商品无货","其他");

  5.    }

  6. }


  7. public class RefundSeason2 implements IRefundSeason{


  8.    @Override

  9.    public List<String> getRefundSeasonList() {

  10.        return ImmutableList.of("不想要了","七天无理由","其他");

  11.    }

  12. }

服务提供者提供接口的一种实现后,我们需要在 META-INF/services 目录中创建一个以接口全限定名的文件 com.lianggzone.design.template_method.example.spi.IRefundSeason 。

图片

这里,文件地内容为实现类的全限定名。

com.lianggzone.design.template_method.example.spi.RefundSeason1

最后,我们通过测试代码来验证下功能。

  1. public class RefundSeasonTest {


  2.    public static void main(String[] args) {

  3.        ServiceLoader<IRefundSeason> loader = ServiceLoader.load(IRefundSeason.class);

  4.        loader.forEach(i -> {

  5.            List<String> refundSeasons = i.invoke();

  6.            System.out.println(refundSeasons);

  7.        });

  8.    }

  9. }

至此,我们实现了一个简单地 Java 的 SPI 功能。事实上,Java 中的 SPI 实现非常简单,我们可以阅读 java.util.ServiceLoader 类。

注意的是,ServiceLoader 每次加载都会生成一份实例,且只能遍历获取所有接口实例,非常浪费资源。同时,获取实现类不够灵活,不能根据某个参数获取对应的实现类,且不支持排序,会出现排序不稳定的情况。因此,很多框架为了解决以上的问题,重新实现了一套更强大的 SPI 机制。例如,Dubbo SPI 自定义了一套 SPI 机制,并把所需的配置文件需放置在 META-INF/dubbo 路径下。与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样可以按需加载指定的实现类。Dubbo SPI 源码分析,参见 http://dubbo.apache.org/zh-cn/docs/sourcecodeguide/dubbo-spi.html


更多相关文章

  1. 快速测试 API 接口的新技能
  2. 异步「背压机制」,谈 RxJava 2.x 解决策略
  3. 「星球精选」如何保证幂等机制
  4. 解决CAS机制中ABA问题的AtomicStampedReference详解
  5. java并发编程CAS机制原理分析
  6. java多线程(4)使用wait/notify机制进行单线程之间的通信
  7. 深入理解java中的泛型机制

随机推荐

  1. Python学习路程day12
  2. Python NameError:全局名称“Form”没有
  3. Hive中使用Python实现Transform时遇到Bro
  4. python学习笔记(3)--爬虫基础教程1
  5. 在NumPy中更改数组边缘的值
  6. 学习用pyhon写hive udf
  7. 创建单独的函数而不是一个大的缓慢处理时
  8. python打印系统所有tcp,udp监听端口及服
  9. python学习(13)————jieba进阶生成词云
  10. 用于搜索和替换大字符串的最快Python方法