spi,ClassLoader,双亲委托模式

2023-11-12

转载:
https://www.cnblogs.com/hiyujie/p/wo-xueJava1ClassLoader-yu-shuang-qin-wei-tuo-mo-sh.html

1、ClassLoader分类
Java虚拟机会创建三类ClassLoader,分别如下

名称 加载 加载路径 父加载器 实现
BootStrap 虚拟机的核心类库 sun.boot.class.path 无 系统
Extension 扩展类库 java.ext.dirs、jre/lib/ext BootStrap Java
System 应用类库 classpath、java.class.path Extension Java
注:父子加载器并非继承关系,也就是说子加载器不一定是继承了父加载器
2、双亲委托模式
其实我觉得把「双亲委托模式」称为「父加载委托模式」更好理解,「双」字把我给弄混了。

「双亲委托模式」指的就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,自己才去加载。


下面是一段ClassLoader的源码,很容易可以看出上述规则:

protected synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException{
// 首先检查该name指定的class是否有被加载
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//如果parent不为null,则调用parent的loadClass进行加载
c = parent.loadClass(name, false);
}else{
//parent为null,则调用BootstrapClassLoader进行加载
c = findBootstrapClass0(name);
}
}catch(ClassNotFoundException e) {
//如果仍然无法加载成功,则调用自身的findClass进行加载
c = findClass(name);
}
}
if (resolve) {
resolveClass©;
}
return c;
}
(1)优点

避免类库重复加载
安全,将核心类库与用户类库隔离,用户不能通过加载器替换核心类库,如String类。
(2)弊端

委托永远是子加载器去请求父加载器,是单向的,即上层的类加载器无法访问下层的类加载器所加载的类:


下层类对于上层类是不可见的
举个例子,假设「BootStrap」中提供了一个接口,及一个创建其实例的工厂方法,但是该接口的实现类在「System」中,那么就会出现工厂方法无法创建在「System」加载的类的实例的问题。拥有这样问题的组件有很多,比如JDBC、Xml parser等。

3、如何解决弊端——使用「SPI」
现在引入一个新的名词「SPI」。

「SPI」 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现的机制。

JDBC本身是Java连接数据库的一个标准,是进行数据库连接的抽象层,由Java编写的一组类和接口组成,接口的实现由各个数据库厂商来完成,不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的「SPI」机制可以为某个接口寻找服务实现。

3、JDBC举例
下面以JDBC为例,介绍「SPI」机制。

在JDBC4.0之前,我们开发有连接数据库的时候,通常会用Class.forName(“com.mysql.jdbc.Driver”)这句先加载数据库相关的驱动,然后再进行获取连接等的操作。而JDBC4.0之后不需要用Class.forName(“com.mysql.jdbc.Driver”)来加载驱动,直接获取连接就可以了,现在这种方式就是使用了Java的「SPI」扩展机制来实现。

(1)接口定义

JDBC在java.sql.Driver只定义了接口。


JDBC中定义的接口
(2)厂商实现

这里以MySQL为例,在mysql-connector-java-6.0.6.jar包里的META-INF/services目录下可以找到一个java.sql.Driver文件,文件内容是一个类名,这个名叫com.mysql.cj.jdbc.Driver的类就是MySQL针对JDBC中定义的接口的实现。



MySQL对JDBC中定义的接口的实现类
(3)如何使用

在我们的应用里面,我们就可以直接连接MySQL了。

Connection conn = DriverManager.getConnection(url,username,password);
显然语句并没有加载实现类,这里就涉及到使用「SPI」扩展机制来查找相关驱动了,接下来,我们结合源码探究一下这是如何实现的。

4、源码解析
关于驱动的查找其实都在DriverManager中,DriverManager位于java.sql包里,用来获取数据库连接,在DriverManager中有一个静态代码块如下:


静态加载
loadInitialDrivers方法用于实例化驱动,由3部分构成:

(1)获取有关驱动的名称


drivers保存驱动的定义
(2)加载并实例化驱动

两个比较关键的地方是ServiceLoader.load, 还有loadedDrivers.iterator,下面结合源码介绍一下:

(A)ServiceLoader.load

ServiceLoader封装了一个自定义加载器loader,还应留意一下下面2个成员,之后会用到:

默认接口路径:PREFIX
实现类的加载迭代器:lookupIterator

ServiceLoader.load(Driver.class)最后会调用构造函数,返回ServiceLoader实例




获取应用层加载器——SystemClassLoader
每一个线程都有自己的ContextClassLoader,默认以SystemClassLoader为ContextClassLoader。通过Thread.currentThread().getContextClassLoader(),可以把一个ClassLoader置于一个线程的实例之中,使该ClassLoader成为一个相对共享的实例,这样即使是启动类加载器中的代码也可以通过这种方式访问应用类加载器中的类了。


多个加载器通过上下文加载器共享
(B)loadedDrivers.iterator

loadedDrivers.iterator方法返回一个迭代器,这个迭代器是「SPI」机制加载实现类的关键,迭代器在iterator()方法内定义:


Iterator的定义,关注hasNext和next方法
「SPI」加载代码的是这样的:


通过一个迭代来加载实现类
执行driversIterator.hasNext时,会调用lookupIterator.hasNext去找的实现类的名字。


driversIterator.hasNext方法

lookupIterator.hasNext方法

lookupIterator.hasNext方法,根据全路径名找实现类的名字
接着会调用lookupIterator.next()去加载这个类:


driversIterator.next方法

lookupIterator.next方法

加载实现类
至此,已经将实现类成功加载。

(3)加载驱动

现在就可以根据第1步获取到的驱动列表来加载实现类了:


调用Class.forName加载类
5、「SPI」的弊端
「SPI」通过循环加载实现类,显而易见,它会把所有的类一同加载,无论有没有用到,这造成了一定的资源浪费:


参考链接
android classloader双亲委托模式
dubbo源码解析-spi(一)
Java中SPI机制深入及源码解析
走出ClassLoader的迷宫

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spi,ClassLoader,双亲委托模式 的相关文章

随机推荐

  • 时序预测

    时序预测 MATLAB实现NARX非线性自回归外生模型时间序列预测 目录 时序预测 MATLAB实现NARX非线性自回归外生模型时间序列预测 效果一览 基本介绍 程序设计 参考资料 效果一览 基本介绍 时序预测 MATLAB实现NARX非线
  • 使用Python和OpenCV构建图像金字塔

    使用Python和OpenCV的图像金字塔 1 效果图 2 什么是图像金字塔 3 依赖 4 源码 参考 这篇博客将介绍如何使用两种方法构造图像金字塔 使用Python和OpenCV构建金字塔 此方法不会在金字塔的每一层使用高斯平滑图像 因此
  • JetBrains new UI 不用申请直接使用

    5月27 JetBrains 发布了新 ui的使用申请 试用链接如下 https www jetbrains com lp intellij new ui preview 填写申请后还需要官方审核 下面介绍一种直接在本地试用的方法 首先需要
  • 在列联分析中的辛普森悖论

    情景 在列联分析中有时会出现辛普森悖论 简述什么是辛普森悖论 它产生的原因是什么 如何防范辛普森悖论 什么是辛普森悖论 在分组比较中都占优势的一方 在总评中有时反而是失势的一方 维基百科 边缘关联和条件关联有相反结论的情况称作辛普森悖论 属
  • Eureka和Zookeeper的区别

    Eureka和Zookeeper的区别 Mysql Oracle SqlServer等关系型数据库遵循的原则是 ACID 原则 即 A 原子性 C 一致性 I 独立性 D 持久性 Redis Mogodb 等非关系型数据库遵循的原则是 CA
  • golang - 键盘输入语句:Scanln()、Scanf()

    在编程中 需要接受用户输入的数据 方法 使用键盘输入语句 步骤 导入 fmt 包 调用 fmt 包的 fmt Scanln 或者 fmt Scanf func Scanln func Scanln a interface n int err
  • dbus-python指南

    菜鸟学dbus python 翻译dbus python指南 错误之处请在所难免 请诸位不吝赐教 多多指正 查看英文原版请点这里 连接总线Connecting to the Bus 方法调用Making method calls 代理对象p
  • 第十三届蓝桥杯省赛C++B组错题笔记

    文章目录 A 九进制转十进制 B 顺子日期 C 刷题统计 D 修剪灌木 E X进制减法 F 统计子矩阵 G 积木画 H 扫雷 I 李白打酒加强版 J 砍竹子 A 九进制转十进制 问题描述 九进制正整数 2022 9 转换成十进制等于多少 答
  • 服务器基础知识

    一 服务器的概念 服务器是计算机的一种 它比普通的计算机运行的更快 负载更高 价格更贵 服务器在网络中为其他的客户机 PC机 智能手机 ATM等终端 提供计算或者应用服务 服务器具有高速的CPU运算能力 长时间的可靠运行 强大的I O外部数
  • QTableWidget用法总汇

    1 QTableWidget不能在mainwindow中随主窗口的大小变化 解决 在表格外部添加布局 代码 tableWidget new QTableWidget tableWidget gt setObjectName QString
  • SpringCloud - Spring Cloud 之 Apollo Config携程阿波罗配置中心(二十一)

    由于Spring Cloud自带的Config 需要配合 Bus 使用 且不能实时刷新 因此市面上出现了很多开元的配置中心 市面上开源的配置中心 Apollo 阿波罗 携程框架部门研发的分布式配置中心 能够集中化管理应用不同环境 不同集群的
  • 图像质量评价指标及MATLAB程序

    指标名称 RMSE 针对一个volume的程序 M 230 行数 N 140 列数 P 11 切片数 计算RMSE volume P origin vector p P recon vector P recon MNP M N P nume
  • 小美的外卖订单编号---牛客周赛 Round 11

    include
  • 职场新人,如何提升自身竞争力?

    在当前就业形势下 如何提高应届生在职场中的竞争力 具有哪些有效的方法和策略可供选择 这是一个备受关注的热点话题 哪些方面会对应届生的职场发展起到关键的推动和支撑作用呢 欢迎大家积极分享你们是如何提升自己的职场竞争力 给即将步入社会的同学一些
  • stm32f103&gd32的usb虚拟串口,打印类printer组合设备

    stm32f103 gd32的usb虚拟串口 打印类printer组合设备 TOC stm32f103 gd32的usb虚拟串口 打印类printer组合设备 由于gd32和stm32f10x系列库和usb库都可以兼任 所以选择st的usb
  • window.showModalDialog() 过时替代方案

    一 window showModalDialog 方法说明 window showModalDialog 方法的作用是创建和展示一个指向特定网页的模态对话框 该方法已经过时 特性已经从 Web 标准中删除 虽然一些浏览器目前仍然支持它 但也
  • 最近很火的ChatGPT和GPT4

    ChatGPT 全名 Chat Generative Pre trained Transformer 美国OpenAI研发的聊天机器人程序 于2022年11月30日发布 ChatGPT是人工智能技术驱动的自然语言处理工具 它能够通过理解和学
  • 深度学习-ubuntu18.04+RTX3080+cuda11.2+cudnn8.1.0下安装polarstream全纪录

    安装 创建一个python3 7的虚拟环境 conda create name polarstream python 3 7 激活虚拟环境 source activate polarstream 以下操作均在虚拟环境中进行 安装与cuda和
  • linxu命令个人使用总结

    find命令查找文件 find name 文件名 curl命令访问网址 cur url sh命令开启服务 sh X sh 进入对应文件夹后 直接输入sh X sh 不在X sh对应文件夹下 则用file path ps A 显示所有进程 p
  • spi,ClassLoader,双亲委托模式

    转载 https www cnblogs com hiyujie p wo xueJava1ClassLoader yu shuang qin wei tuo mo sh html 1 ClassLoader分类 Java虚拟机会创建三类C