• Java SPI 机制源码级深度理解


            SPI 是一种用于动态加载服务的机制。它的核心思想就是解耦,属于典型的微内核架构模式。SPI 在 Java 世界应用非常广泛,如:Dubbo、Spring Boot 等框架。本文从源码入手分析,深入探讨 Java SPI 的特性、原理,以及在一些比较经典领域的应用。

     

     一、SPI 简介

    SPI 全称 Service Provider Interface,是 Java 提供的,旨在由第三方实现或扩展的 API,它是一种用于动态加载服务的机制。Java 中 SPI 机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。

    Java SPI 有四个要素:

    • SPI 接口:为服务提供者实现类约定的的接口或抽象类。

    • SPI 实现类:实际提供服务的实现类。

    • SPI 配置:Java SPI 机制约定的配置文件,提供查找服务实现类的逻辑。配置文件必须置于 META-INF/services 目录中,并且,文件名应与服务提供者接口的完全限定名保持一致。文件中的每一行都有一个实现服务类的详细信息,同样是服务提供者类的完全限定名称。

    • ServiceLoader:Java SPI 的核心类,用于加载 SPI 实现类。ServiceLoader 中有各种实用方法来获取特定实现、迭代它们或重新加载服务。

     

     

    二、SPI 示例

    正所谓,实践出真知,我们不妨通过一个具体的示例来看一下,如何使用 Java SPI。

    2.1 SPI 接口

    首先,需要定义一个 SPI 接口,和普通接口并没有什么差别。

    1. package io.github.dunwu.javacore.spi;
    2. public interface DataStorage {
    3. String search(String key);
    4. }

    2.2 SPI 实现类

    假设,我们需要在程序中使用两种不同的数据存储 ——MySQL 和 Redis。因此,我们需要两个不同的实现类去分别完成相应工作。

    MySQL 查询 MOCK

    1. package io.github.dunwu.javacore.spi;
    2. public class MysqlStorage implements DataStorage {
    3. @Override
    4. public String search(String key) {
    5. return "【Mysql】搜索" + key + ",结果:No";
    6. }
    7. }

    Redis 查询 MOCK 类

    1. package io.github.dunwu.javacore.spi;
    2. public class RedisStorage implements DataStorage {
    3. @Override
    4. public String search(String key) {
    5. return "【Redis】搜索" + key + ",结果:Yes";
    6. }
    7. }

    service 传入的是期望加载的 SPI 接口类型 到目前为止,定义接口,并实现接口和普通的 Java 接口实现没有任何不同。

    2.3 SPI 配置

    如果想通过 Java SPI 机制来发现服务,就需要在 SPI 配置中约定好发现服务的逻辑。配置文件必须置于 META-INF/services 目录中,并且,文件名应与服务提供者接口的完全限定名保持一致。文件中的每一行都有一个实现服务类的详细信息,同样是服务提供者类的完全限定名称。以本示例代码为例,其文件名应该为 io.github.dunwu.javacore.spi.DataStorage,

    文件中的内容如下:

    1. io.github.dunwu.javacore.spi.MysqlStorage
    2. io.github.dunwu.javacore.spi.RedisStorage

    2.4 ServiceLoader

    完成了上面的步骤,就可以通过 ServiceLoader 来加载服务。示例如下:

    1. import java.util.ServiceLoader;
    2. public class SpiDemo {
    3. public static void main(String[] args) {
    4. ServiceLoader serviceLoader = ServiceLoader.load(DataStorage.class);
    5. System.out.println("============ Java SPI 测试============");
    6. serviceLoader.forEach(loader -> System.out.println(loader.search("Yes Or No")));
    7. }
    8. }

    输出:

    1. ============ Java SPI 测试============
    2. 【Mysql】搜索Yes Or No,结果:No
    3. 【Redis】搜索Yes Or No,结果:Yes

     

    参考资料

  • 相关阅读:
    8、DVWA——XSS(Reflected)
    selenium使用
    已解决!Cannot resolve plugin org.apache.maven.plugins:maven-compiler-plugin:3.8.1
    设计模式:依赖倒转原则(Dependency Inversion Principle,DIP)介绍
    LeetCode 每日一题 2022/11/14-2022/11/20
    【甜点】URL化
    异地容灾系统和数据仓库系统设计和体系结构
    进程-死锁的概念/死锁产生的必要条件/死锁的处理策略/死锁的避免
    Pytest系列-使用自定义标记mark(6)
    Speedoffice(Excel)中如何添加页眉页脚
  • 原文地址:https://blog.csdn.net/ejinxian/article/details/127874779