1. 程式人生 > >深入Jar包:Gradle構建可執行jar包與訪問jar包中資料夾與檔案

深入Jar包:Gradle構建可執行jar包與訪問jar包中資料夾與檔案

## 前言 Java的跨平臺功能聽起來很誘人可口,號稱“Write Once,Run Everywhere”,實際上是“Run Once,Debug Everywhere”... 在實際開發過程中還是會遇到各種各樣的坑的,剛剛解決了一系列問題,特地寫個文章總結一下。 ## 使用Gradle構建Jar包 感謝萬能的Gradle,極大提高了Java開發的生產力~ 在Gradle中生成jar包可以使用官方的外掛:`application` 來簡單生成Jar包,同時還有多種不同的配置可以自定義,瞭解詳情請參照Gradle官方文件。 我這裡使用的是一個叫做 `shadow` 的Gradle外掛,把構建jar包的配置都安排得明明白白了,非常的方便! 官方文件:https://imperceptiblethoughts.com/shadow/configuration/#configuring-output-name 下面是 `build.gradle` 配置參考: ```groovy plugins { id 'com.github.johnrengelman.shadow' version '4.0.3' // Apply the java plugin to add support for Java id 'java' // Apply the application plugin to add support for building an application id 'application' } dependencies { implementation 'com.github.jengelman.gradle.plugins:shadow:4.0.3' } // Output to build/libs/name.jar shadowJar { baseName = 'name' classifier = null version = null } apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'java' ``` 具體的配置要依照專案的實際需要來配置~ 設定完 shadow 外掛之後,執行 `gradle build` 就可以在 `build/libs/` 資料夾下面生成你的可執行jar包了,超級方便。 需要更多功能可以檢視shadow官網文件,寫的很清楚。 ## 訪問jar包中的資源 雖然jar包中有各種目錄結構,但是jar包本質仍然是一個檔案,所以不可以用傳統的方法去訪問,像 `File` 類,`Class` 物件的 `getResouce` 方法都不行的。 應該使用 `ClassLoader` 的 `getResourceStream` 方法直接獲取資原始檔的輸入流。 例如: ```java InputStream is=this.getClass().getResourceAsStream("/resource/res.txt"); InputStream is=this.getClass().getClassLoader().getResourceStream("/resource/res.txt"); ``` 注意:`Class`物件和`ClassLoader`物件的`getResourceStream`方法也是有不同的,具體的不同可以檢視這個筆記:[正確獲取Java專案資源](https://app.yinxiang.com/shard/s10/nl/16462562/1227da4b-9da9-4e97-9523-12fb77399ef5?title=Java%20Gradle%E9%A1%B9%E7%9B%AE%E4%B8%AD%E7%9A%84%E8%B5%84%E6%BA%90%E6%AD%A3%E7%A1%AE%E8%8E%B7%E5%8F%96%20-%20Allocator%E7%9A%84CSDN%E5%8D%9A%E5%AE%A2%20-%20CSDN%E5%8D%9A%E5%AE%A2) ## 訪問Jar包中的資料夾 當jar包中的資原始檔很多的時候,不可能一個個輸入名字去獲取,這也太hack了吧,肯定要用自動化的方式來提高生產力。 事實上,訪問jar包中的資料夾是挺麻煩的,不過還是找到了取巧的方法,試了一下還是挺好用的。 (不過最好做一下快取) 程式碼如下: ```java String path = getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); JarFile localJarFile = new JarFile(new File(path)); Enumeration entries = localJarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); String innerPath = jarEntry.getName(); System.out.println(innerPath); } ``` 使用`getClass().getProtectionDomain().getCodeSource().getLocation().getPath();` 來獲取當前jar包的路徑,如果程式碼不在jar包中執行的話,獲取到的就是當前class檔案所在路徑。所以在使用之前最好做一下判斷,看看程式是否在jar包中執行。 ## 關於JavaFX的Media資源問題 JavaFX可以播放音樂,但是和其他Image、Font資源不同的是,Media物件的建構函式只能接受一個String引數(即檔案URL),所以沒辦法使用`getResourceStream`方法把檔案輸入流傳入物件。 我查了一下官網,找到了解決辦法,把檔案URL換成JarURL就可以了,文件:https://docs.oracle.com/javase/6/docs/api/java/net/JarURLConnection.html。 簡單示例: ```java String path = String.format("jar:file:%s!/%s", jarPath, relativePath); Media media = new Media(path); ``` 注意:`relativePath`的形式是 `media/hello.wav` 這