Gradle模塊化項目中使用了非模塊化庫的編譯方法


引文

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則不一樣,需要使用者深入的學習和研究,才能用起來得心應手。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM