motan源码解读之--SPI(Service Provider Interface)实现方式浅析

Motan 是微博技术团队研发的基于 Java 的轻量级 RPC 框架,已在微博内部大规模应用多年,每天稳定支撑微博上亿次的内部调用。Motan 基于微博的高并发和高负载场景优化,成为一套简单、易用、高可用的 RPC 服务框架。Motan框架中主要有register、transport、serialize、protocol几个功能模块,各个功能模块都支持通过SPI进行扩展,下面我们就来看看Motan中SPI是如何实现的。

什么是SPI

SPI(Service Provider Interface)服务提供接口,是指的一种服务发现机制,为某个特定的接口寻找服务的实现。在大规模的软件开发中经常采用这样的机制,实现模块之间基于接口编程,隐藏其实现细节,不同的服务提供商进行扩展实现,最终实现无需修改代码即可调用,实现完全无代码入侵。

 

JDK自带的SPI实例

JDK6以后提供了java.util.ServiceLoader类来实现从配置文件中加载子类或者接口的实现类。

从JDK API文档中我们可以知道实现起来相当简单,只需四步:

1.定义接口

package test;

public interface HelloService {
    public String sayHello();
}

2.写2个实现类,必须是有无构造函数的类

package test;

public class HelloServiceImpl1 implements HelloService {

    @Override
    public String sayHello() {
        return "say hello from HelloServiceImpl1";
    }

}
package test;

public class HelloServiceImpl2 implements HelloService {

    @Override
    public String sayHello() {
        return "say hello from HelloServiceImpl2";
    }

}

3.在资源目录 META-INF/services 中放置提供者配置文件,文件名为文件名称为接口类的完整包路径和类名test.HelloService,

文件必须使用 UTF-8 编码,内容如下:

test.HelloServiceImpl1
test.HelloServiceImpl2

4.测试调用

package test;

import java.util.ServiceLoader;

public class ServiceLoaderTest {

    public static void main(String[] args) {
        ServiceLoader<HelloService> serviceLoader = ServiceLoader.load(HelloService.class);
        for (HelloService service : serviceLoader) {
            System.out.println(service.sayHello());
        }
    }

}

执行后打印结果:

say hello from HelloServiceImpl1
say hello from HelloServiceImpl2

JDK自带的ServiceLoader功能比较弱,只提供了如下几个方法:

1.reload:清除服务实例缓存,重新加载;

2.iterator:延迟方式加载服务,按配置的顺序生成服务实例;

3.load:从上下文加载器(ContextClassLoader)加载。

4.loadInstalled,系统加载器(SystemClassLoader)优先,没有则从上下文加载器加载。

实际上,JDK自带的ServiceLoader并不能满足开发的需求,例如单例服务,按别名使用指定的服务实现,优先级控制等等。

 

Motan的SPI调用实例

Motan底层模块之间调用也是采用SPI模式(com.weibo.api.motan.core.extension.ExtensionLoader),可以根据配置实现调用不同的服务实例。

Motan提供了三个注解:

@Spi 声明接口为SPI服务接口;

@Scope 声明单例模式还是多例模式

@Activation 声明服务优先级(sequence值越小越优先调用)、分组查询、以及是否支持重试调用。

@SpiMeta设定服务别名,没配置则用实现类的类名为别名

ExtensionLoader兼容JDK自带的ServiceLoader,也就是说跟ServiceLoader的配置方式是一样的,在META-INF/services创建服务接口文件,内容为实现类的完整包路径列表。

改写上面的四个步骤:

1.服务接口类

package test;

import com.weibo.api.motan.core.extension.Scope;
import com.weibo.api.motan.core.extension.Spi;

@Spi(scope=Scope.PROTOTYPE) //注解为SPI服务,指定实例方式
public interface HelloService {
    public String sayHello();
}

2.两个实现类

package test;

import com.weibo.api.motan.core.extension.Activation;
import com.weibo.api.motan.core.extension.SpiMeta;

@SpiMeta(name="hello1")
@Activation(sequence=1,key={"hello"})
public class HelloServiceImpl1 implements HelloService {

    @Override
    public String sayHello() {
        return "say hello from HelloServiceImpl1";
    }

}


package test;

import com.weibo.api.motan.core.extension.Activation;
import com.weibo.api.motan.core.extension.SpiMeta;

@SpiMeta(name="hello2")
@Activation(sequence=2,key={"hello"})
public class HelloServiceImpl2 implements HelloService {

    @Override
    public String sayHello() {
        return "say hello from HelloServiceImpl2";
    }

}

3.Motan模式兼容JDK的ServiceLoader,配置是一样的。

4.调用测试

package test;

import java.util.List;

import com.weibo.api.motan.core.extension.ExtensionLoader;

public class ServiceLoaderTest {

    public static void main(String[] args) {
        HelloService service = ExtensionLoader.getExtensionLoader(HelloService.class).getExtension("hello1");//获取指定hello1服务实例
        System.out.println(service.sayHello());
        service = ExtensionLoader.getExtensionLoader(HelloService.class).getExtension("hello2");//获取指定hello2服务实例
        System.out.println(service.sayHello());
        List<HelloService> services = ExtensionLoader.getExtensionLoader(HelloService.class).getExtensions("hello");//对应key分组按sequence优先级排序,为空则所有的
        for (HelloService svc : services) {
            System.out.println(svc.sayHello());
        }
    }

}

执行结果:

say hello from HelloServiceImpl1

say hello from HelloServiceImpl2

say hello from HelloServiceImpl1

say hello from HelloServiceImpl2

 

Motan提供的SPI模式可以实现:

1.单例模式下只会构造一次实例

2.多例模式下在每次获取的时候进行实例化

3.支持手动添加实现类

4.支持ServiceLoader一样的获取所有的实现实例

 

Motan的SPI实现代码解析

motan通过私有静态成员变量ConcurrentMap<Class<?>, ExtensionLoader<?>> extensionLoaders 缓存所有类加载器。

在调用getExtensionLoader(HelloService.class)先检查是否有缓存接口服务类的类加载器,没有则创建一个类加载器,并以以接口类为key放入到extensionLoaders缓存起来,然后开始执行初始化工作,

从META-INF/services目录下找该接口服务类的配置文件,并将配置内容里面的服务实现类读取出来缓存到extensionClasses类列表中以@SpiMeta的配置的name为key(未配置则以实现类名为key),此时并没有创建类的实例,采用的是懒加载模式。

 

getExtension("hello1")时先判断是否是单例模式,如是则判断singletonInstances的中是否有该实例,没有则实例化后缓存起来,并返回;

如果不是单例模式,则从extensionClasses根据spiMeta获取到实现类创建实例返回。

转载于:https://my.oschina.net/11450232/blog/700146

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值