【java 基础】闲话 ClassLoader 和 SPI (一)

文章目录

    • 引子
    • 双亲委派模型
      • 你真的明白了吗?
    • 双亲委派“不够用了”
      • SPI机制
    • 其他琐碎

引子

有别于 java 提供的 IO 模块,java 中的classloader主要是用来加载类的,当然除了加载类,也可以加载资源文件。

那么首先我们会问一个问题,有了 IO 为什么要 classloader?这是我们开启 classloader 大门要弄明白的第一个问题。

java IO 提供了一些常见的功能,比如读文件、写文件,操作字符流、字节流,网络的读写,文件系统操作等等功能,不胜枚举。显而易见,java IO 提供了一些通用方法。

而 classloader 是 JVM 用来按需动态加载资源的工具。之所以有 classloader 有多方面的考虑,首先要解决程序运行时怎么加载类,需要一套机制,这套机制就是我们常说的双亲委派模型。其次是怎么读取资源,比如我们想要读取某个配置文件,或者一张图片(当然读取资源文件我们可以直接用 IO 也不是不可以,殊途同归)。

双亲委派模型

老生常谈的话题,不过也值得讨论一番。java 内建的classloader主要分为 3 类:

  • Bootstrap ClassLoader
  • Extension ClassLoader(又叫 Platform ClassLoader)
  • Application ClassLoader(又叫 System ClassLoader)

Bootstrap ClassLoader: 是最顶层的ClassLoader,负责加载JRE核心库,它是用C++实现的,无法通过Java代码来创建。
Extension ClassLoader:负责加载Java的扩展库。(本质上还是 java 官方提供的,由 java 实现的类库)
Application ClassLoader:负责加载用户类路径下的类。比如我们自己编写的类,引入的第三方 jar 包等。

如下图:我们举一个例子,假设 JVM 要加载类 A,首先会通过 Application ClassLoader 进行加载,这时首先检查其缓存中是否已加载此类,如果加载,则返回。如果缓存中没有类 A,则委托给 Extension ClassLoader进行加载,同样是先检查是否有缓存,如果没有则委托给 Bootstrap ClassLoader 进行加载,同样是检查缓存,如果还是没有,则尝试扫描 JRE 核心库是否有该类,如果有,则加载类,否则返回到 Extension ClassLoader,Extension ClassLoader 扫描其负责的扩展库,如果有,则加载,否则返回到 Application ClassLoader进行加载,Application ClassLoader扫描用户的类路径,如果找到该类,则加载,否则则抛出ClassNotFound 异常。
Image: https://uploader.shimo.im/f/XticuP44pZvBeor5.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MDkyNzg0NzUsImZpbGVHVUlEIjoiNXhrR29HeEVqT0N6MXprWCIsImlhdCI6MTcwOTI3ODE3NSwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjo5NDQ4NDE4fQ.7teiXi8UKNQMlxXtA9Puy-Q_HJAeV0kRoqqgjO3qs8k

你真的明白了吗?

回答下面这个问题:如果类 A 是 Extension ClassLoader 加载,而类 A 中又引入了类 B,那么类 B 会怎么被加载呢?还是从 Appcation ClassLoader 开始加载吗?

答案是否定的,类 B 会从 Extension ClassLoader 开始加载,先委托Bootstrap ClassLoader,如果没找到,则 Extension ClassLoader自己开始加载,如果找不到,则抛出 ClassNotFound,并不会再返回到 Application ClassLoader 进行加载。为什么要这样设计?很简单,留给大家自己思考吧。

双亲委派“不够用了”

有时候默认的双亲委派不够用,举个例子,java 定义了一个数据库标准接口 JDBC,各个数据库厂商会实现这个标准接口,即我们所说的数据库驱动包。大家在学JDBC 的时候应该都写过类似这种代码

Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName");

大家可以尝试将第一句删除掉,你会发现还是可以获取到Connection,这是为什么呢?DriverManager的包名是 java.sql,显然是 jdk的核心包,所以定然不会在其中写入加载某个具体驱动类的代码。所以 java 的开发人员就发明了一种新的方法:SPI(Service Provider Interface)

SPI机制

SPI 的机制很简单,我们还是以数据库驱动为例,首先各个驱动厂商开发对应的驱动包,不过动包会有些特殊,如下图:
在这里插入图片描述
在驱动包的 META-INF/services 下会包含与所要实现驱动名称相同的一个文本文件,文本文件的内容是实现这个驱动的具体类。

然后在执行 DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName");时有以下代码:
Image: https://uploader.shimo.im/f/j7jalpGRyOGJ0Ghs.png!thumbnail?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MDkyNzg0NzUsImZpbGVHVUlEIjoiNXhrR29HeEVqT0N6MXprWCIsImlhdCI6MTcwOTI3ODE3NSwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjo5NDQ4NDE4fQ.7teiXi8UKNQMlxXtA9Puy-Q_HJAeV0kRoqqgjO3qs8k
通过 ServiceLoader.load(Driver.class)去加载驱动。具体怎么加载这里就不说了,无非是扫描上面我们说的META-INF/services目录下的文件,将所有实现了Driver接口的驱动都注册进来。那为什么这里就可以加载到了呢?因为我们在执行ServiceLoader.load(Driver.class)方法时,方法内部是通过 Application ClassLoader 进行加载的,自然可以加载到外部的驱动包了。

那么,如果我引入了多个驱动包呢?系统怎么知道我们用的哪一个?如下图,在 Driver 接口中定义了一个方法:acceptsURL,通过对jdbc:mysql://localhost:3306/dbName这种格式的判断来决定此驱动是不是用户想要的驱动。

在这里插入图片描述
DriverManager 中调用上面实现的acceptsURL 方法:
在这里插入图片描述

其他琐碎

说了那么多,好像跟我们自己平常开发没有多少关系。其实我们也可以利用ClassLoader来加载起源,比如我们想读取一个配置文件。可以用类似ClassLoader.findResource("xxx")或者this.class.getResource("xx")。在一些代码里我们还会看到:ClassLoader cl = Thread.currentThread().getContextClassLoader();这样的代码,这些是在干嘛?后续再跟大家唠唠吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/501419.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【内部消息】24上半年软考可能支持平板、PC和手机等多平台报名

根据内部消息,软考网上报名系统正在改革,之前只能通过PC端报名的,下次报名可能支持平板、手机等多终端进行网上报名了。现在官方并没有确切消息发出,这次变动可能发生在2024上半年,也有可能得到下半年才能实行。以下是…

优质线路、智能加速,向日葵助你轻松进行海外远控

如今,海外远控的需求越来越普遍。无论是企业,还是个人工作室,只要我们的工作生活需要接触到一些“海外元素”,需要进行跨境作业,那么远程控制都可以帮助到我们。 但传统的远程控制在进行海外远控时,往往会…

iconv 更改字符串编码操作

概要 在日常开发中,中文字符乱码是一个经常遇到的问题。在解决此问题时,遇到一个比较好用的字符串编码开源库,在此进行总结。 整体思路流程 iconv官网地址:http://www.gnu.org/software/libiconv/ 这里主要使用的相关接口&…

查看网络连接的netstat

netstat是一个监控TCP/IP网络的非常有用的工具,可以显示路由表、实际的网络连接,以及每一个网络接口设备的状态信息,可以让用户得知目前都有哪些网络连接正在运作。netstat用户显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用…

全链路仿真压测系统

1.项目背景 目前常用的压测工具一般都是针对QPS这一个单一指标进行考量。即使支持编写脚本的工具也只是通过参数化模拟用户。但是实际用户是使用单独设备请求服务器,即一个用户就是一个tcp连接。 所以为了更真实的模拟用户行为,我们需要通过一个tcp连接…

精酿啤酒:从原料到成品的质量控制流程

质量控制是啤酒酿造过程中重要的一环,它涉及到从原料选择到成品生产的每一个环节。Fendi Club啤酒对其质量控制流程有着严格的要求,以确保产品的品质和一致性。 Fendi Club啤酒对原料的选择进行严格把关。他们选用上好、新鲜的麦芽、水和酵母等原料&…

【嵌入式】STM32控制脉冲个数

控制脉冲个数两种方式:中断技术、主从定时器技术。 1.主从模式控制 2.cubemx配置 2.1主定时器 以TIM3为例子。 1)从模式:失能; 2) 触发源:不选择; 3)内部时钟:勾选; 4)输出通道:CH2 pwm模式; 5)单脉冲模式:不选择;

Appium移动端自动化测试-(Java)

目录 环境搭建ADB调试工具adb构成adb工作原理adb常用命令电脑连接多个设备跟模拟器使用adb包名与界面名的概念如何获取包名和界面名文件传输获取app启动时间获取手机日志其他命令 Appium全自动化测试框架(python)冲错了序言 环境搭建Appium客户端安装App…

利用coze 搭建“全功能“微信客服(2)

紧跟上篇 利用coze 搭建"全功能"微信客服(1),不知道来龙去脉自行查阅 先表扬下coze: coze 是国内少数开放平台之一,里面提供各种插件还可以开发工作流,让你可以实现多模态全功能大模型 吐槽 没有API开放接口…

C语言第三十三弹---动态内存管理(上)

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 动态内存管理 1、为什么要有动态内存分配 2、malloc和free 2.1、malloc 2.2、free 3、calloc和realloc 3.1、calloc 3.2、realloc 4、常见的动态内存的错…

【Linux C | 网络编程】套接字选项、getsockopt、setsockopt详解及C语言例子

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…

外汇天眼:外汇市场的交易商、做市商、经纪商有什么区别?

什么是交易商? “外汇交易商指买卖外国汇票的交易公司或个人。 外汇交易商利用自己的资金买卖外汇票据,从中取得买卖价差。 外汇交易商多数是信托公司、银行等兼营机构,也有专门经营这种业务的公司和个人。 ” 外汇交易商从一对儿货币买与卖…