【Dubbo系列】无处不在的SPI之崭露头角

5 篇文章 0 订阅

目录

文章目录

引言

面向接口编程要求我们先根据业务抽象出一个接口,然后根据具体规则建立不同的实现类, 这样一个接口可能会有多个实现类。在具体调用时,就需要指定对应的实现类,当需求发生变化是时,比如新增一个实现或者已有的实现类已经过时,就需要对服务调用端代码进行变更,这就导致了侵入性。

举个例子说明一下,比如一项业务需要根据调用者的需求而使用不同的协议进行处理,那么首先建立一个Protocol接口,他具有两个方法,一个用于获取协议类型,一个进行执行调用:

package com.ryan.myspi;
public interface Protocol {

   // 获取协议类型
   String getProtocolType();

   // 执行调用
   void invoke();
}

第一期根据有两个实现类,一个使用Dubbo协议,另一个使用Hessian协议,写两个实现类:

Dubbo协议实现类DubboProtocol

public class DubboProtocol implements Protocol {

   private static final String protocolType = "dubbo";

   @Override
   public String getProtocolType() {
       return protocolType;
   }

   @Override
   public void invoke() {
       System.out.println ("this is dubbo protocol ");
   }
}

Dubbo协议实现类HessianProtocol

public class HessianProtocol implements Protocol {

   private static final String protocolType = "hessian";

   @Override
   public String getProtocolType() {
       return protocolType;
   }

   @Override
   public void invoke() {
       System.out.println ("this is hessian protocol ");
   }
}

接下类,建立客户端调用类,首先把系统中的实现类加载到缓存中,再调用时,根据调用端需求判断使用哪一个协议实现类:

public class ProtocolClient {

   private final static Map<String,Protocol> RPOTOCOL_LIST = new HashMap<> ();

   static {
       RPOTOCOL_LIST.put ("dubbo", new DubboProtocol ());
       RPOTOCOL_LIST.put ("hessian", new HessianProtocol ());
   }

   public void doInvoke(String protocolType) {
       Protocol protocol = ProtocolClient.RPOTOCOL_LIST.get (protocolType);
       if (protocol != null) {
           protocol.invoke ();
       }
   }
}

大家想一下,如果以后需求发生了变化,比如需要根据http协议新增一个HttpProtocol实现类同时不再支持DubboProtocol协议,那么就需要侵入ProtocolClient的static中的代码块。如果需要多个协议进行处理时,比如一个请求需要经过一个协议链,那么维护将更加复杂。

我们需要一种方式消除调用者对实现类的依赖,最好可以在配置中对实现类(也可以称为组件)进行动态的添加和替换,这时候*SPI(Service Provider Interface)*便闪亮登场了。

SPI实现

SPI通过在META-INF/services 中增加一个配置文件,该文件名使用服务接口全限定名(the fully-qualified binary name of the service’s type)表示,其中内容包含一系列服务提供者类的全限定名(a list of fully-qualified binary names of concrete provider classes, one per line).

看了上面这段想必一定很晕吧,接下来对之前的例子进行重构来看一下SPI的实现:

还是两个Protocol接口的实现类DubboProtocol大哥和HessianProtocol小弟,把这两兄弟放到配置文件中,文件名为com.ryan.myspi.Protocol中,实现了这步后整个文件目录如下:

├─main
│  ├─java
│  │  └─com
│  │      └─ryan
│  │          └─myspi
│  │                  DubboProtocol.java
│  │                  HessianProtocol.java
│  │                  Protocol.java
│  │                  
│  └─resources
│      └─META-INF
│          └─services
│                  com.ryan.myspi.Protocol
│                  

com.ryan.myspi.Protocol配置文件中添加两个实现类的全限定名:

com.ryan.myspi.DubboProtocol
com.ryan.myspi.HessianProtocol

接下来在调用客户端中,替换掉原静态代码块中的内容,使用ServiceLoader来加载实现类

public class SpiProtocolClient {

   private final static Map<String,Protocol> RPOTOCOL_LIST = new HashMap<> ();

   static {
       // 使用ServiceLoader
       ServiceLoader<Protocol> serviceLoader = ServiceLoader.load (Protocol.class);
       Iterator<Protocol> iterator = serviceLoader.iterator ();
       while (iterator.hasNext ()) {
           Protocol itr = iterator.next ();
           RPOTOCOL_LIST.put (itr.getProtocolType (), itr);
       }
   }

   /**
    * 描述:调用相关协议
    * @since JDK 1.8
    */
   public void doInvoke(String protocolType) {
       Protocol protocol = SpiProtocolClient.RPOTOCOL_LIST.get (protocolType);
       if (protocol != null) {
           protocol.invoke ();
       }
   }
}

可以看到通过使用SPI,以后需要增加协议实现类,或者替换已有的协议实现类时,只要对com.ryan.myspi.Protocol文件进行修改即可。

小结

本文简单介绍了下SPI的使用,当然JDK中的SPI还有一些缺陷,比如会对所有的实现类进行初始化并加载,如果没用的类比较多的话,会很耗资源并且启动时间也会很久。因此Dubbo对SPI进行了加强,接下来会做进一步分析。

如果需要原文代码的朋友请关注公众号,并在后台回复:spi1

推荐阅读:

Mybatis拦截器源码深度解析

线程池的前世今生

了解更多请关注微信公众号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值