当前位置: 首页 > news >正文

Java SPI的本质

SPI的目的

让调用者调用更加方便

SPI和Interface的本质区别

1 SPI的接口定义和接口实现是分开的(不同jar包),SPI就是一种调用场景(没别的)

2 Interface的接口定义和接口实现都在服务提供方

SPI如何实现

1 底层使用JDK的ServiceLoader

(1)配置文件:META-INF/services/接口名称

(2)配置文件内容为具体的实现类的全限定名

2 具体业务逻辑在SPI接口定义方实现

 没有SPI的时候(以com.mysql.jdbc.Driver为例)

1 没有SPI:需要手动实例化所需的Driver,新加入一种实现就需要再写一遍

2 有SPI:

Class.forName(jdbcClass);
Connection conn = DriverManager.getConnection(jdbcUrl, userName, password);

DriverManager源码

(1)用ServiceLoader加载所有Driver类,调用load方法只是清空了所有的providers

public class DriverManager {
    
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
    

    private static void loadInitialDrivers() {
        ...
        ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
        Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                while(driversIterator.hasNext()) {
                    // 调用ServiceLoader的next()
                    driversIterator.next();
                }
            } catch(Throwable t) {
                // Do nothing
            }    
        ...
        
    }
}

(2)核心代码在ServiceLoader中的hasNextService和nextService。

     a) hasNextService会调用系统classLoader(AppClassLoader)去所有classpath中d的META-INF/services/ + 接口名文件下找到类名

     b) nextService会调用Class.forName加载类

public final class ServiceLoader<S>
    implements Iterable<S> {

    public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }


    private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                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);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }
        

    private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }


}

 

相关文章:

  • 计算机网络——分层结构
  • 微信小程序 behaviors
  • 前端修罗场,祝您中秋快乐
  • 国际航运管理简答题-题库
  • 重新认识 IP地址
  • Matplotlib光速入门-从安装到常用实战
  • 课设总结【硬件课设】
  • 你这个视频背景太假了?
  • 一文搞懂cookie、session、token、jwt、OAuth
  • 《Go Web 编程》之第4章 处理请求
  • ZYNQ之中断机制
  • Java JDK path环境变量配置
  • linux编译安装 php-nginx-mysql
  • 个人收入理财App的设计与实现
  • 【OpenStack云平台】网络控制节点 HA 集群配置
  • php的引用
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • 77. Combinations
  • Angular数据绑定机制
  • canvas绘制圆角头像
  • CentOS7简单部署NFS
  • CSS盒模型深入
  • ES6之路之模块详解
  • Less 日常用法
  • Linux各目录及每个目录的详细介绍
  • Magento 1.x 中文订单打印乱码
  • PHP变量
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • TCP拥塞控制
  • 模型微调
  • 思否第一天
  • 通过几道题目学习二叉搜索树
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  •  一套莫尔斯电报听写、翻译系统
  • 应用生命周期终极 DevOps 工具包
  • 智能合约开发环境搭建及Hello World合约
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • !!Dom4j 学习笔记
  • #### go map 底层结构 ####
  • #if和#ifdef区别
  • (+4)2.2UML建模图
  • (06)金属布线——为半导体注入生命的连接
  • (10)STL算法之搜索(二) 二分查找
  • (4.10~4.16)
  • (C)一些题4
  • (ibm)Java 语言的 XPath API
  • (补)B+树一些思想
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (七)c52学习之旅-中断
  • (转)fock函数详解
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET CORE使用Redis分布式锁续命(续期)问题
  • .NET DataGridView数据绑定说明
  • .Net Web项目创建比较不错的参考文章
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖