美文网首页
从携程Apollo客户端源码学到的SPI知识(二)

从携程Apollo客户端源码学到的SPI知识(二)

作者: 稻草鸟人 | 来源:发表于2020-04-24 21:04 被阅读0次

知识点:SPI(Service Provider Interfaces)到底是啥玩意,简单一句话概括就是用来被第三方实现的 API。有点抽象,我们先仿照Apollo客户端写个简单例子吧

场景举例

假设有一个短信平台需要对接多家SMS渠道用来发送短信。当然这个场景不一定要用SPI机制来实现,毕竟条条大路通罗马。本案例只是用来介绍SPI的使用方式方法。

工程目录

fg-great-spi.png

fg-spi-sms-interface 是接口部分,提供了一个默认实现,fg-spi-sms-provider是自定义的一个实现

fg-spi-sms-interface

接口部分的代码结构如下,本模块假设为通用实现

fg-spi-sms-interface.png

我们首先提供一个短信发送的接口或者抽象类,下面我们新建一个接口如下:

package com.example.fg.sms;

/**
 * @author cattle -  稻草鸟人
 * @date 2020/4/23 下午12:49
 */
public interface MessageServiceProvider {

    /**
     *  发送短息
     * @param message 短信内容
     */
    void sendMessage(String message);

}

其次我们对这个接口做一个默认实现如下:

package com.example.fg.sms;

/**
 * @author cattle -  稻草鸟人
 * @date 2020/4/23 下午12:57
 */
public class DefaultMessageServiceProvider implements MessageServiceProvider {
    @Override
    public void sendMessage(String message) {
        System.out.println("default:::" + message);
    }
}

再次,我们在resources/META-INF/services目录下新建一个文件,名为com.example.fg.sms.MessageServiceProvider此名字和接口同名,内容为com.example.fg.sms.DefaultMessageServiceProvider是我们的默认实现

最后,我们就可以通过ServiceLoader.load。例如,通过如下代码实现执行我们默认方法实现的方法

package com.example.fg.sms;

import java.util.ServiceLoader;

/**
 * @author cattle -  稻草鸟人
 * @date 2020/4/23 下午12:52
 */
public class MessageServiceFactory {

    private ServiceLoader<MessageServiceProvider> serviceProviders = ServiceLoader.load(MessageServiceProvider.class);

    public void sendMessage(String message) {

        for (MessageServiceProvider serviceProvider : serviceProviders) {
            serviceProvider.sendMessage(message);
        }

    }
}

接口测试

package com.example.fg.sms;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MessageServiceFactoryTest {

    @Test
    void sendMessage() {
        MessageServiceFactory factory = new MessageServiceFactory();
        factory.sendMessage("hello spi");
    }
}

输出:

default:::hello spi

fg-spi-sms-provider

本模块是另外一个实现,用来模拟某供应商针对接口实现自己的短信发送方法。

fg-spi-sms-provider.png

接口实现

package com.example.fg.sms;

/**
 * @author cattle -  稻草鸟人
 * @date 2020/4/23 下午1:06
 */
public class MyMessageServiceProvider implements MessageServiceProvider {
    @Override
    public void sendMessage(String message) {
        System.out.println("MyMessage:::" + message);
    }
}

本模块我们依然要在resources/META-INF/services目录下新建一个名为com.example.fg.sms.MessageServiceProvider内容就是我们自己的实现,即com.example.fg.sms.MyMessageServiceProvider

接口测试

package com.example.fg.sms;

import org.junit.jupiter.api.Test;

class MessageServiceFactoryTest {

    @Test
    void sendMessage() {
        MessageServiceFactory factory = new MessageServiceFactory();
        factory.sendMessage("hello spi");
    }
}

输出:

MyMessage:::hello spi
default:::hello spi

其他SPI场景

目前开源的很多框架里面,如果大家仔细观察的话,SPI用到了非常多的地方,比如dubbo,motan,日志处理框架,还有JDBC Driver等等。

总结

经过上面的案例其实已经很清楚了,写一个接口,然后自己去实现,另外在resources/META-INF/services目录下新建一个文件夹,内容是具体的实现。然后通过ServiceLoader.load之后调用具体的实现。

思考

  1. 多个实现的情况下,如何只执行其中一种实现呢?比如MyMessageServiceProviderDefaultMessageServiceProvider两个实现只执行其中一个sendMessage方法

  2. apollo-client中对于SPI的具体应用,和我们当前的例子是否有差别呢?

源码

https://github.com/cattles/fucking-great-spi

相关文章

网友评论

      本文标题:从携程Apollo客户端源码学到的SPI知识(二)

      本文链接:https://www.haomeiwen.com/subject/isvqwhtx.html