java實(shí)現(xiàn)讀取jar包中配置文件的幾種方式
概述
在編程的某些情況下,我們需要讀取jar包中的文件,這種情況要區(qū)別于我們平時(shí)使用類加載器讀取配置文件,這個(gè)時(shí)候配置在jar包中,就能讀取到,但是配置文件也可以不在jar包中,只要放在Class-Path下就行了,所以這種情況下,我更愿意把它稱之為:讀取Class-Path下的配置文件。而我今天描述的比較明確,就是要讀取jar包中的文件。這種需求可能不多,但是我碰見(jiàn)了,并且發(fā)現(xiàn)了幾種,今天全部羅列分享一下。
目前有3種:
- JarFile
- URL
- ClassLoader
定義接口
因?yàn)橛泻脦追N方式,那就直接定義個(gè)接口:
public interface JarReader { /** * 讀取jar包中的文件 * @param jarPath jar包路徑 * @param file jar包中的文件路徑 * @return 文件內(nèi)容,轉(zhuǎn)換成字符串了,其它需求也可以轉(zhuǎn)換成輸入流。 * @throws IOException */ String readFromJar(String jarPath,String file) throws IOException; }
jar包讀取器,jar包中的文件讀取出來(lái)。
通過(guò)JarFile讀取
JarFile是java自帶的一種讀取jar包的API,很多人應(yīng)該用過(guò),我就直接貼代碼了。public class JarFileJarReader implements JarReader { ? ? @Override ? ? public String readFromJar(String jarPath,String file) throws IOException { ? ? ? ? JarFile jarFile=null; ? ? ? ? try { ? ? ? ? ? ? jarFile=new JarFile(jarPath); ? ? ? ? ? ? JarEntry jarEntry=jarFile.getJarEntry(file); ? ? ? ? ? ? InputStream input=jarFile.getInputStream(jarEntry); ? ? ? ? ? ? return IOUtils.toString(input,"UTF-8"); ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? throw e; ? ? ? ? } finally { ? ? ? ? ? ? IOUtils.closeQuietly(jarFile); ? ? ? ? } ? ? } }
代碼也比較簡(jiǎn)單,重點(diǎn)就是最后一定要把jarFile這個(gè)對(duì)象關(guān)閉一下,中間的輸入流都可以不用關(guān)閉。
不過(guò)我在寫(xiě)這段代碼之前,從我的個(gè)人經(jīng)驗(yàn)上來(lái)說(shuō),JarFile好像更多是用來(lái)讀取清單文件(MANIFEST.MF)的,可能是見(jiàn)這種情況比較多,當(dāng)然它的用途肯定遠(yuǎn)不止如此。
因此我順便寫(xiě)了一下讀取清單文件的代碼:
public void getManiFest(String jarPath) throws IOException { ? ? ? ? JarFile jarFile=null; ? ? ? ? try { ? ? ? ? ? ? jarFile=new JarFile(jarPath); ? ? ? ? ? ? Manifest manifest=jarFile.getManifest(); ? ? ? ? ? ? if (manifest!=null){ ? ? ? ? ? ? ? ? //獲取Class-Path ? ? ? ? ? ? ? ? String classPaths = (String) manifest.getMainAttributes().get(new Attributes.Name("Class-Path")); ? ? ? ? ? ? ? ? if (classPaths != null && !classPaths.isEmpty()) { ? ? ? ? ? ? ? ? ? ? String[] classPathArray = classPaths.split(" "); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? //獲取JDK版本 ? ? ? ? ? ? ? ? String jdkVersion = (String) manifest.getMainAttributes().get(new Attributes.Name("Build-Jdk")); ? ? ? ? ? ? ? ? //還可以獲取其它內(nèi)容,比如Main-Class等等 ? ? ? ? ? ? } ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? throw e; ? ? ? ? } finally { ? ? ? ? ? ? IOUtils.closeQuietly(jarFile); ? ? ? ? } ? ? }
通過(guò)URL讀取
java自帶的URL是支持讀取jar中的文件的,協(xié)議是jar,表示方式的話是用"/!"把jar包和文件區(qū)分一下。代碼如下:
public class URLJarReader implements JarReader { @Override public String readFromJar(String jarPath, String file) throws IOException { JarURLConnection jarURLConnection=null; try { URL fileUrl=ParseUtil.fileToEncodedURL(new File(jarPath)); URL jarUrl=new URL("jar", "", -1, fileUrl + "!/"); URL moduleUrl = new URL(jarUrl, ParseUtil.encodePath(file, false)); jarURLConnection = (JarURLConnection)moduleUrl.openConnection(); return IOUtils.toString(jarURLConnection.getInputStream(),"UTF-8"); } catch (IOException e) { throw e; } finally { if (jarURLConnection!=null){ try { jarURLConnection.getJarFile().close(); } catch (IOException ignore) { } } } } }
ParseUtil的幾個(gè)方法是我在看java源碼的時(shí)候看見(jiàn)的,用來(lái)處理一些不規(guī)則的文件路徑。
我剛開(kāi)始用URL的時(shí)候,就出現(xiàn)了一個(gè)內(nèi)存泄漏的文件,讀取完了以后,jar包被占用,死活不能刪除,剛才開(kāi)始把輸入流給關(guān)閉了,也沒(méi)有用。然后想到了類加載器里面有close方法,然后去看了一下,找到了上述代碼中的finally塊的代碼。這樣就可以把占用問(wèn)題解決了,仔細(xì)看的話,會(huì)發(fā)現(xiàn),getJarFile.close(),因此本質(zhì)上還是關(guān)閉了JarFile,和上面是一樣的。
通過(guò)ClassLoader
這個(gè)也是借鑒了我們平時(shí)讀取配置文件的方式,借用一下ClassLoader來(lái)讀取。
public class ClassLoaderJarReader implements JarReader { @Override public String readFromJar(String jarPath, String file) throws IOException{ URLClassLoader urlClassLoader=null; try { URL fileUrl=ParseUtil.fileToEncodedURL(new File(jarPath)); urlClassLoader=new URLClassLoader(new URL[]{fileUrl},null); InputStream inputStream=urlClassLoader.getResourceAsStream(file); if (inputStream==null){ throw new FileNotFoundException("not find file:"+file+" in jar:"+jarPath); }else{ return IOUtils.toString(inputStream,"UTF-8"); } } catch (IOException e) { throw e; } finally { IOUtils.closeQuietly(urlClassLoader); } } }
代碼也是比較簡(jiǎn)單,最后把ClassLoader關(guān)閉一下就行了。
關(guān)于類加載器的讀取方式,我很早之前就看過(guò)了,它本質(zhì)上用的就是上面兩種方式結(jié)合起來(lái)讀取文件的。
總結(jié)
這幾種方式的話,其實(shí)沒(méi)有什么區(qū)別,從開(kāi)發(fā)角度來(lái)說(shuō)的話,比較推薦第三種,因?yàn)槭莏ava自帶的功能,也是比較完善,也簡(jiǎn)單,也不容易出錯(cuò),而且它內(nèi)部用的就是前面兩種。不過(guò)從資源消耗上面來(lái)說(shuō),我猜測(cè),前面兩種應(yīng)該占優(yōu),不過(guò)我也不糾結(jié)這個(gè),沒(méi)去研究。
有時(shí)候我們或許有另外一種需求,讀取jar中的jar中的文件,這個(gè)在一些場(chǎng)景下,會(huì)使用到。最起碼spring-boot確實(shí)是用到了,很早之前我看過(guò)它的實(shí)現(xiàn),它就是把URL重寫(xiě)了一下,支持了一下多個(gè)"/!"表達(dá)式,就能夠支持這種情況了。
到此這篇關(guān)于java實(shí)現(xiàn)讀取jar包中配置文件的幾種方式的文章就介紹到這了,更多相關(guān)java 讀取jar包配置文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis結(jié)果集映射與生命周期詳細(xì)介紹
結(jié)果集映射指的是將數(shù)據(jù)表中的字段與實(shí)體類中的屬性關(guān)聯(lián)起來(lái),這樣 MyBatis 就可以根據(jù)查詢到的數(shù)據(jù)來(lái)填充實(shí)體對(duì)象的屬性,幫助我們完成賦值操作2022-10-10Java工廠模式之簡(jiǎn)單工廠,工廠方法,抽象工廠模式詳解
這篇文章主要為大家詳細(xì)介紹了Java工廠模式之簡(jiǎn)單工廠、工廠方法、抽象工廠模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-02-02LinkedBlockingQueue鏈?zhǔn)阶枞?duì)列的使用和原理解析
這篇文章主要介紹了LinkedBlockingQueue鏈?zhǔn)阶枞?duì)列的使用和原理解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10詳解Spring bean的注解注入之@Autowired的原理及使用
之前講過(guò)bean注入是什么,也使用了xml的配置文件進(jìn)行bean注入,這也是Spring的最原始的注入方式(xml注入).本文主要講解的注解有以下幾個(gè):@Autowired、 @Service、@Repository、@Controller 、@Component、@Bean、@Configuration、@Resource ,需要的朋友可以參考下2021-06-06eclipse創(chuàng)建一個(gè)基于maven的web項(xiàng)目詳細(xì)步驟
開(kāi)始學(xué)習(xí)maven,并用maven創(chuàng)建了第一個(gè)屬于自己的web項(xiàng)目,下面這篇文章主要給大家介紹了關(guān)于eclipse創(chuàng)建一個(gè)基于maven的web項(xiàng)目的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12Maven?Pom?文件中的隱式依賴導(dǎo)致Jar沖突問(wèn)題
這篇文章主要介紹了Maven?Pom?文件中的隱式依賴導(dǎo)致Jar沖突問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12BufferedInputStream(緩沖輸入流)詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了BufferedInputStream緩沖輸入流的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Java中?springcloud.openfeign應(yīng)用案例解析
使用OpenFeign能讓編寫(xiě)Web?Service客戶端更加簡(jiǎn)單,使用時(shí)只需定義服務(wù)接口,然后在上面添加注解,OpenFeign也支持可拔插式的編碼和解碼器,這篇文章主要介紹了Java中?springcloud.openfeign應(yīng)用案例解析,需要的朋友可以參考下2024-06-06Java高性能本地緩存框架Caffeine的實(shí)現(xiàn)
本文主要介紹了Java高性能本地緩存框架Caffeine的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02一文詳細(xì)講解Java時(shí)間格式轉(zhuǎn)換
這篇文章主要介紹了Java時(shí)間格式轉(zhuǎn)換的相關(guān)資料,文中詳細(xì)介紹了SimpleDateFormat(適用于Java8之前)和java.time(適用于Java8及之后)的用法,需要的朋友可以參考下2024-12-12