java開發(fā)讀取嵌套jar包中的文件
讀取 jar 包中的 jar 文件
例如有一個(gè) Jar 包 A.jar, 他的目錄文件如下圖
A.jar |--B.jar |--Test.class |--.....
通過 new JarFile(A.jar)
可以等到 A.jar 對應(yīng)的對象,可以遍例 A.jar 中的所有文件,Jar 包中的文件以 JarEntry
的形式保存數(shù)據(jù) ,全碼大致如下:
public void testJar() throws IOException { JarFile jarFile = new JarFile("C:\\Users\\Mzoro\\Desktop\\operation-1.1.jar"); System.out.println(jarFile.getName()); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); System.out.println(entry.getAttributes()); System.out.println(name); } }
但是 如果想繼續(xù)遍歷 B.jar 中的文件就不行了,需要其他方法,有一個(gè)活生生的例子是 spring-boot 打包后的 jar 的運(yùn)行過程
對應(yīng)的 java 類的大致說明
一、嵌套 jar 的數(shù)據(jù)與信息獲取方面
Archive,對 jar 包,或者目錄的抽象
對 jar 包的抽象就是常見的,將 spring-boot 工程發(fā)布成可執(zhí)行 jar 包,和嵌套其中的 jar 包與或目錄,具體實(shí)現(xiàn)是 org.springframework.boot.loader.archive.JarFileArchive
;可以通過 JarFileArchive
實(shí)例獲取它的子目錄或者嵌套的 jar
org.springframework.boot.loader.Launcher, 真正的 springboot 啟動(dòng)類
這是一個(gè)抽象類,作用如下
創(chuàng)建具體的 Archive 實(shí)例( Archive createArchive()
),JarFileArchive 還是 WarFileArchive, 具體是通過 class 文件的協(xié)議名來判定具體實(shí)例了。如果 jar 包啟動(dòng),class 文件 url 前面的協(xié)議是以 jar:file 開頭的;如果是 war 包,因?yàn)榇翱跁?huì)將 war 解壓之后 再啟動(dòng),所以 class 文件 url 的協(xié)議是 file://
創(chuàng)建上下文的 ClassLoader; 用于加載嵌套包中的 class 與 classes 文件夾中的 class。為什么要設(shè)置上下文 classLoader 呢?因?yàn)閱?dòng) springboot 的 jar 包時(shí)的 classpath 只有 jre 環(huán)境與 springboot 的 jar 包,如果用啟動(dòng) Launcher 的 ClassLoader 會(huì)找不到類,所以要設(shè)置上下文 ClassLoader 為 LanuchedURLClassLoader
這個(gè)類聲明了一個(gè) abstract List<Archive> getClassPathArchives()
方法,抽象的,目的是返回 ClassPath 下的 jar 包或者目錄,為什么設(shè)置為 abstract 呢?因?yàn)?war 與 jar 的運(yùn)行時(shí)依賴的 lib 是在不同目錄下的,class 也在不同目錄下,同時(shí)還需要過濾掉一些不必要的 jar 包或者 war 包中的東西,比如 MANIFEST.MF
文件對加載類是沒有用的,所有 Archive 集合中沒有必要包含它。這個(gè)方法的返回值會(huì)在構(gòu)造 LancherURLClassLoader 時(shí)傳入,在 findClass 時(shí) 會(huì)在這些 Archive 代表的目錄或者文件中查找 Class 文件
org.springframework.boot.loader.jar.JarFile
這個(gè)類繼承自 java.util.jar.JarFile
, 主要重寫的方法 Enumeration<java.util.jar.JarEntry> entries()
; 它對應(yīng)的是 springboot jar 中嵌套的 jar , 這個(gè)類的主要作用是在構(gòu)造時(shí)創(chuàng)建一個(gè) JarFileEntries
,這個(gè)類主要重寫了 entries () 方法,而這個(gè)方法返回的 Enumeration 是依靠 JarFile 持有的 JarFileEnties 獲得的
private JarFile(RandomAccessDataFile rootFile, String pathFromRoot, RandomAccessData data, JarEntryFilter filter, JarFileType type) throws IOException { super(rootFile.getFile()); this.rootFile = rootFile; this.pathFromRoot = pathFromRoot; CentralDirectoryParser parser = new CentralDirectoryParser(); this.entries = parser.addVisitor(new JarFileEntries(this, filter)); parser.addVisitor(centralDirectoryVisitor()); this.data = parser.parse(data, filter == null); this.type = type; }
org.springframework.boot.loader.jar.JarFileEntries
這個(gè)類的作用非常重要,它代表一個(gè) jar 包中的所有 Entries, 并且這個(gè)類在構(gòu)建時(shí)就保存了這個(gè) jar 包中所有 Entry 的文件流信息,所有在通過這個(gè)類的對象獲取具體的 JarEnty 對象時(shí),JarEnty 對象就可以包含 entry 對應(yīng)的文件的真正的流數(shù)據(jù)。在 definedClass 方法的入?yún)?,byte [] 是一個(gè)必須的參數(shù)
個(gè)人覺得難就難在這里,如何計(jì)算 jar 包中每個(gè)文件的流的偏移量,文件大小等這些信息
二、ClassLoader 方面
LaunchedURLClassLoader
它繼承自 URLClassLoader,這個(gè)類相對 LaunchedURLClassLoader 沒有太大區(qū)別,主要的區(qū)別在于對包的定義,因?yàn)樵诙x包時(shí)要從嵌套 jar 中獲取 MANIFEST.MF 信息
org.springframework.boot.loader.jar.Handler
因?yàn)?URLClassLoader 在獲取 Class 文件時(shí)需要通過 URL 對象來獲取,而這個(gè) url 具體如何獲?。ɑ蛘哒f打開 Connection),可以指定 Handler,
org.springframework.boot.loader.jar.Handler
就是為了打開嵌套 jar 連接延生的; 它是實(shí)現(xiàn)了java.net.URLStreamHandler
的類,URLStreamHandelr 只有一個(gè)抽象方法,就是URLConnection openConnection(URL url)
JarURLConnection
可以通過這個(gè)類獲取 InputStream 了,有了 InputStream 就可以等到 definedClass 所需的 byte [] 參數(shù),而這個(gè)
JarURLConnection
獲取 InputStream 的方法是通過構(gòu)建 JarURLConnection 時(shí)的JarFile
來獲取的,JarFile 獲取 InputStream 的方法是通過其持有的JarFileEntries
來獲取的,JarFileEntries
的獲取方法就是讀取 jar 包的偏移量讀取二進(jìn)制數(shù)據(jù)
總結(jié)
看了一通代碼最后感覺還是不能自己實(shí)現(xiàn),難點(diǎn)在于讀取嵌套 jar 包流的問題上在
疑問
代碼上感覺 spring-boot-loader
只處理了一層嵌套,不知道能不能處理多層的,當(dāng)然,可能也沒有人這么用;如果可以的話,那么除了 springboot 工程,其他工程有沒有可能也使用這種方式進(jìn)行打包并進(jìn)行任意層的嵌套呢?感覺好蠢的想法
參考
以上就是java開發(fā)讀取嵌套jar包中的文件的詳細(xì)內(nèi)容,更多關(guān)于java讀取嵌套jar包文件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java8(291)之后禁用了TLS1.1使JDBC無法用SSL連接SqlServer2008的解決方法
這篇文章主要介紹了Java8(291)之后禁用了TLS1.1使JDBC無法用SSL連接SqlServer2008的解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03Java分布式ID中Snowflake雪花算法應(yīng)用實(shí)現(xiàn)
Snowflake算法作為一種高效且易于實(shí)現(xiàn)的分布式ID生成方案,能夠很好地滿足分布式系統(tǒng)中對全局唯一ID的需求,本文就來介紹一下Java分布式ID中Snowflake雪花算法應(yīng)用實(shí)現(xiàn),感興趣的可以了解一下2024-07-07SpringBoot日志進(jìn)階實(shí)戰(zhàn)之Logback配置經(jīng)驗(yàn)和方法
本文給大家介紹在SpringBoot中使用Logback配置日志的經(jīng)驗(yàn)和方法,并提供了詳細(xì)的代碼示例和解釋,包括:滾動(dòng)文件、異步日志記錄、動(dòng)態(tài)指定屬性、日志級別、配置文件等常用功能,覆蓋日常Logback配置開發(fā)90%的知識點(diǎn),感興趣的朋友跟隨小編一起看看吧2023-06-06springboot整合swagger3報(bào)Unable to infer base&nbs
這篇文章主要介紹了springboot整合swagger3報(bào)Unable to infer base url錯(cuò)誤問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05SpringBoot搭建Dubbo項(xiàng)目實(shí)現(xiàn)斐波那契第n項(xiàng)詳解
這篇文章主要講解了“SpringBoot+Dubbo怎么實(shí)現(xiàn)斐波那契第N項(xiàng)”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)吧2022-06-06解決IDEA service層跳轉(zhuǎn)實(shí)現(xiàn)類的快捷圖標(biāo)消失問題
這篇文章主要介紹了解決IDEA service層跳轉(zhuǎn)實(shí)現(xiàn)類的快捷圖標(biāo)消失問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Java線程和操作系統(tǒng)線程的關(guān)系解讀
這篇文章主要介紹了Java線程和操作系統(tǒng)線程的關(guān)系解讀,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06