资讯 小学 初中 高中 语言 会计职称 学历提升 法考 计算机考试 医护考试 建工考试 教育百科
栏目分类:
子分类:
返回
空麓网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
空麓网 > 计算机考试 > 软件开发 > 后端开发 > Java

[04][02][02] SPI 机制

Java 更新时间: 发布时间: 计算机考试归档 最新发布

[04][02][02] SPI 机制

文章目录
  • 1. 基本概念
  • 2. 使用场景
  • 3. 使用要求
  • 4. JDK SPI 实现
  • 5. Spring SPI 实现

1. 基本概念

SPI (Service Provider Interface), 是 Java 提供的一套用来被第三方实现或者扩展的 API, 它可以用来启用框架扩展和替换组件

Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制

2. 使用场景
  • 数据库驱动
  • 日志 SLF4J
  • Spring 使用 SPI
  • Dubbo 使用 SPI, 但对原生 SPI 做了封装, 允许用户扩展实现 Filter 接口
3. 使用要求

要使用 SPI, 需要遵循如下约定

  • SPI 的实现类中必须有一个无参的构造方法
  • 当服务提供者提供了接口的实现, 在 jar 包的 META-INF 目录下创建一个以“接口全限定名”为命名的文件, 内容为实现类的全限定名
  • JDK 中路径时 META-INF/services/
  • Spring 中配置是 META-INF/spring.factories
  • 接口实现类所在的 jar 包放在主程序的 classpath 中
  • 通过 Loader 类将 META-INF 目录下的配置文件加载, 解析文件中的全限定名类名, 将类加载到 JVM
  • JDK 中是使用的是 ServiceLoader 加载 META-INF/services/ 下的文件
  • Spring 中使用的是 SpringFactoriesLoader 加载 META-INF/spring.factories
4. JDK SPI 实现

对数据库驱动源码分析

MySQL 驱动是 om.mysql.cj.jdbc 包下 Driver 实现了接口 JDK 的 java.sql.Driver, 并在 META-INF/services 目录下创建文件名为 java.sql.Driver 的文件, 在文件里填写实现类的全路径信息 com.mysql.cj.jdbc.Driver

// 实现 JDK 的 java.sql.Driver 类
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    // SPI 的实现类中必须有一个无参的构造方法
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

查看 java.util.ServiceLoader 源码

public final class ServiceLoader implements Iterable {
    private static final String PREFIX = "META-INF/services/";

    // 代表被加载的类或者接口
    private final Class service;

    // 用于定位,加载和实例化providers的类加载器
    private final ClassLoader loader;

    // 创建ServiceLoader时采用的访问控制上下文
    private final AccessControlContext acc;

    // 缓存providers,按实例化的顺序排列
    private LinkedHashMap providers = new LinkedHashMap<>();

    // 懒查找迭代器
    private LazyIterator lookupIterator;
    ......
}

ServiceLoader 可以跨越 jar 包获取 META-INF/services 目录下的配置文件

try {
    String fullName = PREFIX + service.getName();
    if (loader == null)
        configs = ClassLoader.getSystemResources(fullName);
    else
        configs = loader.getResources(fullName);
} catch (IOException x) {
    fail(service, "Error locating configuration files", x);
}

获取实现类的全类名后, 通过反射方法 Class.forName() 加载类对象

5. Spring SPI 实现

Spring 的 SPI 实现是由 org.springframework.core.io.support 包下的 SpringFactoriesLoader 实现的, 配置文件路径是 META-INF/spring.factories 与 JDK 有差异

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	public static  List loadFactories(Class factoryClass, @Nullable ClassLoader classLoader) {}

    private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
        ...
        // 扫描 jar 包下 META-INF/spring.factories 文件
        Enumeration urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        ...
    }
}

在 Spring 的 spring-data-redis 中对 JDK 和 Spring 都做了 SPI 实现

转载请注明:文章转载自 http://www.konglu.com/
免责声明:

我们致力于保护作者版权,注重分享,被刊用文章【[04][02][02] SPI 机制】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理,本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2023 成都空麓科技有限公司

ICP备案号:蜀ICP备2023000828号-2