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

本文介绍了Java的Service Provider Interface(SPI)机制,用于实现服务的注册和发现,以解耦系统组件。详细讲解了SPI的原理,包括ServiceLoader的使用,以及在Android组件化方案中的应用,通过AutoService和APT生成配置文件实现业务模块间的交互。同时,讨论了SPI的优缺点,并给出了实际项目中的代码实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

format,png

 195e44fdb21e50c8caacf7961e27502c.gif 

本文字数:13862

预计阅读时间:35分钟

1、前言

在开题之前大家有没有这个疑惑,就是随着业务的不断发展和壮大,我们的工程结构以及代码量越来越大,交织在一起,你中有我,我 中有你,导致项目结构分工不是很明确,职责不清晰,如果线上有紧急问题需要排查的时候,我们可能就手忙脚忙了,有时候也不知道问题出现在哪个模块,导致问题排查效率低下,同时不利于项目复盘和明确职责,又或者老板想看下业务成功率,服务成功率等等一些数据的时候,我们可能为了方便直接在代码里面写了,随着业务的不断迭代,新接手的同事可能就会绞尽脑汁,这段代码到底是干什么的,为什么要写在这里(也许这段代码根本没有任何的实际业务含义,就是一个埋点或者数据统计方面的代码)等等这些问题,今天我们隆重的给大家介绍一种新的解决方案来解决这些问题,这个方案就是SPI,全称是Service Provider Interface,当然大家可能说也有别的方案,是,确实是有别的方案,条条道路通罗马,OK,我们进入正题。

2、什么是SPI

SPI(Service Provider Interface),是JDK提供的一套用来被第三方实现或者扩展的API,它是一种JVM层面的服务注册发现机制, 可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用。SPI机制主要思想是将装配的控制权移到程序之外,在组件化设计中这个机制尤其重要,其核心思想就是解耦。

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

  • SPI和API区别。867ebe16dceed9a07e0e513ac09e575e.pngAPI

85eb695cc782e8e3835e97bf0a5ff429.png   SPI 

为了更清楚的把这个问题讲明白,我们使用具体的图来说明SPI与API区别,上图就很清晰的说明了这两个问题

一般模块之间通信基本上都是通过接口,那我们在服务调用方和服务实现方(也称服务提供者)之间引入一个“接口概念”。当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是API,这种接口和实现都是放在实现方的。接口和实现方属于同一个模块,密切不可分割。当接口存在于调用方这边时,就是SPI,由接口调用方确定接口规则,然后由不同的具体业务去根据这个规则对这个接口进行实现,从而提供服务,举个通俗易懂的例子:一个电脑制造公司,设计好了充电器标准图纸以后,那么接下来就可以把这个图纸分发给不同的厂商去生产,最后只要严格按照图纸要求,就可以生产合格的商品。通过上面的图2和图3以及配合上面的文字介绍,相信大家应该很非常清楚API和SPI的区别了。

3、SPI作用

SPI的发现能力是不需要依赖于其他类库,最重要的作用就是解耦 主要实现方式是。

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

4、实现原理

a09bd3f55ef7ef32437c7aa9582a9b84.png
源码分析:
  • ServiceLoader源码

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);
}
 
private static void fail(Class<?> service, String msg)
throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() + ": " + msg);
}
 
private static void fail(Class<?> service, URL u, int line, String msg)
throws ServiceConfigurationError
{
fail(service, u + ":" + line + ": " + msg);
}
 
  
private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
List<String> names)
throws IOException, ServiceConfigurationError
{
String ln = r.readLine();
if (ln == null) {
return -1;
}
int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值