引文
Gradle的配置文件有點像Makefile,都是用腳本來控制代碼的編譯。大體上Gradle跟Maven差不多,因為最終都是把項目文件整理成javac的編譯參數,用jar來打包,區別在於形式上的不同,Gradle的編譯選項使用的是腳本(Groovy或Kotlin),Maven用的是Xml。據Gradle的官方稱,Gradle的編譯性能要比Maven快上幾倍,剛好最在做一個項目的時候,因為在Maven中加了一個依賴,導致IDE一直處於繁忙狀態,只能去任務管理器中把進程結束掉,恢復原來的配置。雖然這個依賴是可有可無的,但促使我有了想嘗試一下Gradle的念頭。
換成Gradle本身倒不是太難的事情,直接在Maven項目中執行Gradle Init就可以將Maven項目轉換成Gradle項目,寫了幾個測試項目,也沒什么問題,但我的項目是跟jfx有關的,這不免要用到java的模塊化功能,於是問題就來了。Java的模塊化在用Maven構建的時候很正常,一切換到Gradle就不行了,報找不模塊的錯誤。分析了一下,是因為報錯的模塊都沒有經過模塊化的,按照Java官方的說法,如果庫沒有模塊化,就放到classpath下,在module-info.java里使用jar包的名稱來作為自動模塊的模塊名。這在Maven下不用特別設置,就可以順利編譯,但在Gradle下邊是不行的。大概是Maven作了自動處理,而Gradle沒有。翻了無數遍Gradle的文檔,終於發現Gradle遇到這種情況確實是需要特別處理的,需要在build.gradle里加一個叫extra-java-module-info的插件,然后使用這個插件申明一下那些未模塊化的類庫,這樣才可以在module-info.java中正常地引入。
以下部分演示如何Gradle項目中使用非模塊化(未命名模塊unnamed module)庫,希望能幫到那些使用Gradle的小伙伴們。
准備工作
安裝Gradle工具
由於不是本文的重點,具體過程省略,可以參考以上鏈接完成Gradle的安裝和配置。
生成項目
如何生成項目,參考以上鏈接。
代碼演示
新建一個項目
我創建一個叫test的項目。項目結構跟Maven差不多,只是項目根目錄下多了一些跟gradle有關的東西。我們先看一下build.gradle文件,跟構建相關的內容基本上就在這個文件里,初始的build.gradle是個樣子:
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java Library project to get you started.
* For more details take a look at the Java Libraries chapter in the Gradle
* User Manual available at https://docs.gradle.org/6.5/userguide/java_library_plugin.html
*/
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:29.0-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.13'
}
修改倉庫
由於牆的原因,默認的倉庫下載依賴項會很慢,所以要改國內有鏡像,我這里用的阿里Maven倉庫鏡像。
將:
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
改成:
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
maven{
url 'https://maven.aliyun.com/repository/central'
}
mavenCenter()
}
加入依賴包
在dependencies中加入fastjson的依賴包:
dependencies {
//...省略已有內容
implementation 'com.alibaba:fastjson:1.2.70'
// ...省略已有內容
}
此時在命令行運行一下gradlew build是沒有什么問題的。
模塊化
為了實現模塊化,需要在源碼根目錄(${project_path}\src\main\java)下加入module-info.java文件。
module-info.java
module test {
exports test;
requires com.google.common;
requires fastjson;
}
再次運行gradlew build出錯了:
E:\projects\test1\src\main\java\module-info.java:3: 錯誤: 找不到模塊: com.google.common
requires com.google.common;
^
E:\projects\src\main\java\module-info.java:4: 錯誤: 找不到模塊: fastjson
requires fastjson;
出現這個問題的原因是沒有把依賴項放到module-path里去。在使用javac編譯的時候會有這一個參數:
--add-modules <模塊>(,<模塊>)* 除了初始模塊之外要解析的根模塊; 如果
為 ALL-MODULE-PATH, 則為模塊路徑中的所有模塊。
在gradle中的配置是這樣的,用文本編輯器打開build.gradlew,加上這么一段內容:
java {
modularity.inferModulePath = true
}
然后我們再次編譯,會發現此時錯誤少了一個。
E:\projects\ebiz\java\test1\src\main\java\module-info.java:4: 錯誤: 找不到模塊: fastjson
requires fastjson;
^
1 個錯誤
這其實很好理解,guava-29.0-jre.jar的在MANIFEST.MF文件中指定了Automatic-Module-Name: com.google.common,說明已經是自動模塊化了的,所以在模塊化項目中直接導入,而fastjson-1.2.70.jar的在MANIFEST.MF文件中沒有這一行。這也說明阿里的開發人員並不CARE模塊化這玩意,在MANIFEST.MF中加一行Automatic-Module-Name不願意去做。
配置非模塊化庫(也叫未命名模塊庫unnamed module library)
gradle自身不會管你引用的庫是不是模塊化的,它都統一處理的,為了將非模塊化庫獨立出來,此時我們就要使用一個模塊的工具插件了——extra-java-module-info。
我們在plugins里加上一行。
id "de.jjohannes.extra-java-module-info" version "0.1"
然后再加上這一段:
extraJavaModuleInfo {
// This does not have to be a complete description (e.g. here 'org.apache.commons.collections' does not export anything here).
// It only needs to be good enough to work in the context of this application we are building.
module('commons-math3-3.6.1.jar','org.apache.commons','3.6.1')
module('failureaccess-1.0.1.jar','failureaccess','1.0.1')
module('jsr305-3.0.2.jar','jsr305','3.0.2')
module('listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar','listenablefuture-9999.0-empty-to-avoid-conflict-with-guava','9999.0')
module('j2objc-annotations-1.3.jar','j2objc-annotations','1.3')
module('hamcrest-core-1.3.jar','hamcrest-core','1.3')
module('fastjson-1.2.70.jar','fastjson','1.2.70') {
exports("com.alibaba.fastjson")
}
automaticModule("guava-29.0-jre.jar", "com.google.common")
}
再次運行gradlew build,結果顯示成功。
到這里,在Gradle模塊化項目中引入非模塊化的演示部分就結束了。
總結
雖然Gradle出來的時候也不短了,但相對Maven來說,用戶量也少了不少。由於Maven的廣泛使用,該踩的坑都被前人給踩了,使用Maven的時候,可以做到基本上不讀文檔,完全靠粘貼復制都能混日子,而Gradle則不一樣,需要使用者深入的學習和研究,才能用起來得心應手。