SPI原理以及SPI在Android中的实战

1.背景

在开题之前大家有没有这个疑惑,就是随着业务的不管发展和壮大,我们的工程结构以及代码量越来越大,交织在一起,你中有我,我

中有你,导致项目结构分工不是很明确,职责不清晰,如果线上有紧急问题需要排查的时候,我们可能就手忙脚忙了,有时候也不知道问

题出现在那个模块,导致问题排查效率低下,同时不利于项目复盘和明确职责,又或者老板想看下业务成功率,服务成功率等等一些数据的时

候,我们可能为了方便直接在代码里面写了,随着业务的不管迭代,新接手的同事可能就会绞尽脑汁,这段代码到底是干什么的,为什么

要写在这里(也许这段代码根本没有任何的实际业务含义,就是一个埋点或者数据统计方面的代码)等等这些问题,今天我们隆重的给大家介

绍一种新的解决方案来解决这些问题,这个方案就是SPI,全称是Service Provider Interface,当然大家可能说也有别的方案,是,确实是

有别的方案,条条道路通罗马,OK,我们废话不多说,进入正题。

2.什么是SPI

SPI(Service Provider Interface),是JDK提供的一套用来被第三方实现或者扩展的API,它是一种JVM层面的服务注册发现机制,

可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用。SPI机制主要思想是将装配的控制权移到程序之外,在组件化

设计中这个机制尤其重要,其核心思想就是解耦。

2.1: SPI整体机制

如下 图1所示:

图1

Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制,最核心的思想就是服务注册+服务发现

2.2: SPI和API区别

那么讲到这里,很多人可能就有疑问,说这个和API调用有什么区别呢,OK,为了更清楚的把这个问题讲明白,我们使用具体的图来说明

SPI与API区别:

API

图2所示

图2

SPI

图3所示

图3

一般模块之间通信基本上都是通过接口,那我们在服务调用方和服务实现方(也称服务提供者)之间引入一个“接口概念”。

当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 API ,这种接口和实现都是放在实现方的。

接口和实现方属于同一个模块,密切不可分割。

当接口存在于调用方这边时,就是 SPI ,由接口调用方确定接口规则,然后由不同的具体业务去根据这个规则对这个接口进行实现,从而提供服务,

举个通俗易懂的例子:一个电脑制造公司,设计好了充电器标准图纸以后,那么接下来就可以把这个图纸分发给不同的厂商去生产,最后只有严格按

照图纸要求,就可以生产合格的商品。

通过上面的图2和图3以及配合上面的文字介绍,相信大家应该很非常清楚API和SPI的区别了.

3.SPI作用

SPI的发现能力是不需要依赖于其他类库,主要实现方式是:

  • java.util.ServiceLoader#load JDK自身提供的加载能力

最重要的作用就是:解耦

4.实现原理

4.1: 源码分析

public final class ServiceLoader<S>

implements Iterable<S>

{

//配置文件所在的包目录路径

private static final String PREFIX = "META-INF/services/";

// 接口名称

private final Class<S> service;

// 类加载器

private final ClassLoader loader;

// The access control context taken when the ServiceLoader is created

// Android-changed: do not use legacy security code.

// private final AccessControlContext acc;

//providers就是不同实现类的缓存,key就是实现类的全限定名,value就是实现类的实例

private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

// //内部类LazyIterator的实例

private LazyIterator lookupIterator;

public void reload() {

providers.clear();

lookupIterator = new LazyIterator(service, loader);

}

private ServiceLoader(Class<S> svc, ClassLoader cl) {

service = Objects.requireNonNull(svc, "Service interface cannot be null");

loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;

// Android-changed: Do not use legacy security code.

// On Android, System.getSecurityManager() is always null.

// acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;

reload();

}

private static void fail(Class<?> service, String msg, Throwable cause)

throws ServiceConfigurationError

{

throw new ServiceConfigurationError(service.getName() + ": " + msg,

cause);

}

猜你喜欢

转载自blog.csdn.net/qq_18757557/article/details/128551981
SPI