【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.pswp.cn/news/711641.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/711641.shtml
英文地址,請注明出處:http://en.pswp.cn/news/711641.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java基礎 - 14 Java的Deque之Deque、BlockingDeque、LinkedBlockingDeque、ArrayDeque

Java 中的 Deque(雙端隊列)是一種具有隊列和棧特性的數據結構,它允許在兩端進行插入和刪除操作。Deque 接口是 Java 集合框架中的一部分,它定義了雙端隊列的基本操作。 BlockingDeque 接口: BlockingDeque 接口是 Deq…

docker搭建git服務器

1、docker搭建git服務器 總體思路:服務端通過docker搭建git服務器,客戶端創建git的賬戶及公鑰密鑰; 1)服務端# 創建容器 # --privileged 獲得完整的root權限 # /usr/sbin/init 啟動容器執行的第一個命令 以便可以使用systemctl命…

2024年FPGA可以進嗎

2024年,IC設計FPGA行業仍有可能是一個極具吸引力和活力的行業,主要原因包括: 1. 技術發展趨勢:隨著5G、人工智能、物聯網、自動駕駛、云計算等高新技術的快速發展和廣泛應用,對集成電路尤其是高性能、低功耗、定制化芯…

【UE 材質】制作加載圖案(2)

在上一篇(【UE 材質】制作加載圖案)基礎上繼續實現如下效果的加載圖案 效果 步驟 1. 復制一份上一篇制作的材質并打開 2. 添加“Floor”節點向下取整 除相同的平鋪數 此時的效果如下 刪除如下節點 通過“Ceil”向上取整,參數“Tiling”默認…

教師招聘和事業編d類有什么區別嗎

每年都有大批懷揣教育夢想的年輕人,站在職業的十字路口,對未來充滿期許與疑惑。他們中的許多人都會面臨這樣一個問題:教師招聘和事業編D類,到底有什么區別?今天,就讓我來為你揭開這兩者的神秘面紗。 別被這…

【大數據】Flink SQL 語法篇(五):Regular Join、Interval Join

《Flink SQL 語法篇》系列,共包含以下 10 篇文章: Flink SQL 語法篇(一):CREATEFlink SQL 語法篇(二):WITH、SELECT & WHERE、SELECT DISTINCTFlink SQL 語法篇(三&…

ubuntu系統下大數據服務器磁盤調優測試記錄

一、背景 在kvm虛擬機ubuntu操作系統大數據平臺測試的過程中,遭遇了磁盤I/O性能的瓶頸,因有cpu綁核操作,故有做隔核操作驗證是否是綁核影響的磁盤I/O,后又對磁盤進行透傳以及掛內存盤等操作; 二、磁盤介紹 2.1 磁盤…

『NLP學習筆記』圖解 BERT、ELMo和GPT(NLP如何破解遷移學習)

圖解 BERT、ELMo和GPT(NLP如何破解遷移學習) 文章目錄 一. 前言二. 示例-句子分類三. 模型架構3.1. 模型輸入3.2. 模型輸出四. BERT VS卷積神經網絡五. 詞嵌入新時代5.1. 簡要回顧詞嵌入Word Embedding5.2. ELMo: 上下文語境很重要5.2.1. ELMo的秘密是什么?5.3. ULM-FiT:將遷…

藍橋杯Python B組練習——斐波那契數列

一、題目 定義 斐波那契數列(Fibonacci sequence),又稱黃金分割數列,因數學家萊昂納多斐波那契(Leonardo Fibonacci)以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數…

Linux x86平臺獲取sys_call_table

文章目錄 前言一、根據call *sys_call_table來獲取二、使用dump_stack三、根據MSR_LSTAR寄存器四、使用sys_close參考資料 前言 Linux 3.10.0 – x86_64 最簡單獲取sys_call_table符號的方法: # cat /proc/kallsyms | grep sys_call_table ffffffff816beee0 R sy…

隨想錄算法訓練營第四十七天|198.打家劫舍、213.打家劫舍II、337.打家劫舍III

198.打家劫舍 public class Solution {public int Rob(int[] nums) {if(nums.Length0){return 0;}if(nums.Length1){return nums[0];}int[] dpnew int[nums.Length1];dp[0]nums[0];dp[1]Math.Max(nums[0],nums[1]);for(int i2;i<nums.Length;i){dp[i]Math.Max(dp[i-2]nums[…

什么是 HTTPS 證書?作用是什么?

HTTPS 證書&#xff0c;即超文本傳輸安全協議證書&#xff08;Hypertext Transfer Protocol Secure&#xff09;&#xff0c;是網站安全的關鍵組成部分。它通過 SSL/TLS 加密協議&#xff0c;確保用戶與網站之間的數據傳輸是加密和安全的。 什么是 HTTPS 證書&#xff1f; HT…

使用Docker -compose啟動自定義jar包

步驟1&#xff1a;編寫docker-compose.yml文件 首先我們需要編寫一個docker-compose.yml文件來定義我們的服務傳到我們的云服務器上 以下是一個示例&#xff1a; version: 3 services:app:build:context: .dockerfile: Dockerfileports:- 8080:8080volumes:- ./app.jar:/app…

可視化圖表:水球圖,展示百分比的神器。

Hi&#xff0c;我是貝格前端工場的老司機&#xff0c;本文分享可視化圖表設計的水球圖設計&#xff0c;歡迎老鐵持續關注我們。 一、水球圖及其作用 水球圖是一種特殊的可視化圖表&#xff0c;它主要用于展示百分比或比例的數據&#xff0c;并以水球的形式進行呈現。水球圖的作…

2023面試題

目錄 題目 1:JVM 整體結構是什么樣的? 8 題目 3:Object 類有哪些方法? 11 題目 4:靜態變量與實例變量區別? 11 題目 5:String 類的常用方法有哪些? 11 題目 6:數組有沒有 length()方法?String 有沒有 length() 12 題目 7:String、StringBuffer、StringBuilder 的區別…

【k8s 訪問控制--認證與鑒權】

1、身份認證與權限 前面我們在操作k8s的所有請求都是通過https的方式進行請求&#xff0c;通過REST協議操作我們的k8s接口&#xff0c;所以在k8s中有一套認證和鑒權的資源。 Kubenetes中提供了良好的多租戶認證管理機制&#xff0c;如RBAC、ServiceAccount還有各種策路等。通…

集合篇之ArrayList

一、源碼如何分析&#xff1f; 1.成員變量 2.構造方法 3.關鍵方法 一些添加的方法。 二、debug看源碼 我們給出下面代碼&#xff1a; public void test01() {ArrayList<Integer> list new ArrayList<>();list.add(1);for (int i 2; i < 10; i) {list.add(i…

H5:段落標簽與換行標簽

目錄 一.前言 二.正文 1.段落標簽 2.換行標簽 三.結語 一.前言 學習前端&#xff0c;從此起飛&#xff0c;愿你堅持&#xff0c;直至等頂。 二.正文 1.段落標簽 <p></p> p為段落標簽&#xff0c;由英文paragraph簡寫而來&#xff0c;用于將一段某一部分文本&am…

算法練習第九天|232.用棧實現隊列、225. 用隊列實現棧

熟悉棧和隊列的方法&#xff1b;熟悉棧和隊列的數據結構&#xff0c;不涉及算法 232.用棧實現隊列 import java.util.Stack; class MyQueue {//負責進棧的Stack<Integer> stackIn;//負責出棧的Stack<Integer> stackOut;public MyQueue() {stackIn new Stack<&…

Rocketmq 入門介紹

從零手寫實現 mq 詳細介紹一下 rocketmq RocketMQ 是由阿里巴巴開發的分布式消息隊列系統&#xff0c;它是一個低延遲、高可靠、高吞吐量的消息中間件。 RocketMQ 最初是作為阿里巴巴的內部項目進行開發的&#xff0c;后來成為了 Apache 軟件基金會下的頂級項目&#xff0c;以…