0


SPI 及其在 Springboot 中的使用

SPI (Service Provider Interface) 作为 Spring 系列一项至关重要的技术,在源码中有着众多的使用场景,对于我们自己编写各类 starter,也有很大的借鉴作用。下面我们将通过阅读源码的方式,带大家了解和运用 SPI 的相关技术。

1. 什么是 SPI

了解过 JDBC 的小伙伴都知道,Java 原本有一套自己的 SPI加载机制,例如通过加载

META-INF/services/

下的各类数据库 Driver,能够在定义了一系列接口的情况下,屏蔽不同数据库的细节,以此实现代码不变,通过配置文件任意切换数据库的功能。
Spring 中的 SPI 其实也运用了相同的思想。通过定义接口规范,Spring 可以将需要的实现类全部载入并实例化。这一点在各类 spring 组件中运用十分广泛。

autoconfigure的配置

spring-boot-autoconfigure

包为例,在它的配置文件

META-INF/spring.factories

中,定义了一系列接口和实现类,在 spring 启动时,就能通过该配置文件,获取到所有在此定义的实现类的全限定名并实例化。

2. SPI 在 Springboot 的使用举例

下面我们以 springboot 启动流程中,第一个出现 SPI 的地方为例。

在这里插入图片描述
springboot 的静态 run 方法,实际上还是直接 new 了一个新的

SpringApplication

。我们可以在此构造函数中发现,在这里获取了一些接口的实例并保存到了

SpringApplication

的成员变量中,供后续启动步骤的某个时刻调用。

跟随

getSpringFactoriesInstances

方法,我们最终来到了

org.springframework.core.io.support.SpringFactoriesLoader

forDefaultResourceLocation

方法

在这里插入图片描述
从这里我们可以看到为什么 spring 会加载来自

META-INF/spring.factories

的配置信息,因为这个方法默认使用了该路径作为参数,所以当你需要 spring 帮你加载时,必须要按照他的默认读取路径来编写配置文件。当然,如果你选择自己加载,完全可以调用其他重载方法,传入自定义的配置文件路径。

接下来经过一步缓存方法后,我们来到了最主要的

protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation)

方法,代码我就不贴了,感兴趣的小伙伴可以自己查阅。该方法的主要作用就是读取了所有的配置文件,最终解析并返回一个

Map<String, List<String>>

,该 Map 的 key 是接口的全限定名,value是一个List, 里面存放了所有该接口实现类的全限定名。

在这里插入图片描述

到了这一步,我们就拿到了所有我们配置的接口,以及对应实现类的全限定名。此外,

SpringFactoriesLoader

还提供了利用全限定名,反射创建实例的方法。至此,我们就完成了从配置文件,到接口实现类实例化的过程。

值得一提的是,一些接口,例如

org.springframework.boot.SpringApplicationRunListener

,在创建其实现类,并希望 spring 帮我们实例化的时候,我们需要按照它的参数要求创建一个指定的构造器,这是因为 spring 在上述步骤利用反射创建实例时,传入了指定的构造器参数。当无法利用此参数找到对应构造器时,就会抛出异常。

注意:最新版本的 Springboot 不必创建一个指定参数的构造器,存在无参构造器(默认构造器)的情况下会默认执行此构造器,并且不会抛出异常。

在这里插入图片描述

3. 利用 SPI 帮助实例化

接下来我们尝试利用 Springboot 的SPI,帮助我们实例化一些对象。

我们以

org.springframework.boot.SpringApplicationRunListener

这个 Springboot 自带的接口为例,创建一个实现类

publicclassMySpringApplicationRunListenerimplementsSpringApplicationRunListener{publicMySpringApplicationRunListener(){System.out.println("This is my listener. ");}}

并且创建

META-INF/spring.factories

,写入要加载的类信息

org.springframework.boot.SpringApplicationRunListener=\
smoketest.test.MySpringApplicationRunListener

运行程序,成功打印,这意味着 Springboot 成功调用无参构造器实例化了该类。在这里插入图片描述
当然,如上文所说,你也可以自定义配置路径,并且手动实例化。

在这里插入图片描述
(完)

标签: spring boot spring java

本文转载自: https://blog.csdn.net/weixin_44512127/article/details/127977729
版权归原作者 lcy2317 所有, 如有侵权,请联系我们删除。

“SPI 及其在 Springboot 中的使用”的评论:

还没有评论