動態

詳情 返回 返回

開源之夏經驗分享|Koupleless 社區魏照華:開源精神是場永不停歇的接力 - 動態 詳情

開源之夏經驗分享|Koupleless 社區魏照華:開源精神是場永不停歇的接力

魏照華

Computer Science
Koupleless 社區貢獻者

就讀於 University of Wolloging,Computer Science 專業研究生。

本文 5428 字,預計閲讀 14 分鐘

今天 SOFAStack 邀請到了開源之夏 2024 Koupleless 社區的中選學生魏照華同學!在本項目中,他參與完成了 ​Koupleless 模塊打包插件 Gradle 版本​。希望他分享的這段經歷,能讓更多人瞭解到 Koupleless 開源社區,感受開源的魅力~

項目鏈接​:https://summer-ospp.ac.cn/org/prodetail/2495a0374?lang=zh&list=pro

項目信息

項目名稱​:Koupleless 模塊打包插件 Gradle 版本

項目導師​:梁櫟鵬

項目背景​:

在 Koupleless 模塊化研發框架裏,模塊是普通 SpringBoot 或者 Java 代碼工程,通過使用 SOFAArk 模塊打包插件構建出 jar 包,這個 jar 包就是我們的模塊構建產物,通過在一個基座 JVM 裏部署多個這樣的模塊 jar 包,運行起來的時候,​1 個模塊 = 1 個 ClassLoader + 1 個 SpringContext​,通過多 ClassLoader 和 SpringContext 的隔離和共享來為模塊開發提供最極致的研發效能和資源效能。

而 SOFAArk 打包插件就是將傳統 Maven 工程打包成 SOFAArk 模塊 jar 包的插件,並且支持設置類的隔離與共享的配置能力(對象的隔離與共享不在該插件裏配置)。但是當前 SOFAArk 打包插件只支持 Maven 版本,不支持 Gradle 版本,導致很多 Gradle 的用户無法使用 SOFAArk 來享受到 Koupleless 模塊化研發框架的收益。

項目實現思路

SOFAArk 目前擁有兩種打包插件,分別為 sofa-ark-maven-plugin 和 sofa-ark-plugin-maven-plugin。前者負責將普通的 Java 工程或者 SpringBoot 工程打包成標準格式的 Ark 包或者 Ark Biz 包,後者是將一個或者多個普通的 jar 包打包成標準格式的 Ark Plugin。這三類 jar 包都有自己特定的格式和內容。具體的內容可以參考如下:

  • Ark :https://www.sofastack.tech/projects/sofa-boot/sofa-ark-ark-jar/
  • Ark Biz:https://www.sofastack.tech/projects/sofa-boot/sofa-ark-ark-biz/
  • Ark Plugin:
    https://www.sofastack.tech/projects/sofa-boot/sofa-ark-ark-plugin/

這三類包從包含類別上而言,Ark 包是包含種類最多的包,除了 MANIFEST.MF 文件之外,還包含了業務的 Ark Biz 包、Ark 容器,以及啓動相關的內容。Ark 包的 Maven 打包過程,在 SOFAArk 的官方文檔^​[1]​^中,已經有了詳細的解析。

從這份文檔中可以瞭解到,通過自定義 Maven 插件,按照 SOFAArk 中所定義的格式,重新編排後的 jar 包,就是我們想要得到的目標產物。

回到 Gradle 打包插件上,通過了解 Maven 插件打包的具體實現過程,使用 Gradle 提供的插件 API,來實現上述過程即可。為此,需要去了解 Gradle 的生命週期,依賴管理,自定義插件等內容,進行後續開發工作。

總覽整個項目,實現過程中主要處理如下內容:

  • 用户的配置項
  • 項目依賴項
  • 啓動類和容器類
  • 插件調試

用户配置項

SOFAArk 打包插件提供了非常豐富的用户配置選項,通過設置這些配置選項,能夠打包出滿足不同需求的產物內容。不同的配置項會對應不同的特性,這些特性極大地增強了項目構建的靈活性與定製性。並且使得用户能夠根據項目實際的運行環境、性能需求以及部署要求等因素,精細地調整打包產物的內容與形式。

Gradle 提供了 extensions 來讓插件實現可配置,這一機制提供了一種優雅且強大的方式來定製插件行為。通過自定義 extension 類或者接口,聲明需要的配置屬性,便可以在插件中使用配置項。

//定義extensions
abstract public class ArkExtension {
   abstract public Property<Boolean> getAttach();
    // 定義一個字符串集合類型的屬性excludes,用於指定需要在打包過程中排除的依賴
   abstract public SetProperty<String> getExcludes();
}
//在插件中使用
class ArkPlugin implements Plugin<Project> {
    void apply(Porject project){
      ArkExtension arkExtension = project.getExtensions().create("arkPlugin", ArkPluginExArkExtension.class);
 }
}

這裏需要注意的是,使用 extensions 的擴展方式,如果對插件的屬性設置了默認值,那麼在配置結束階段,是沒有辦法獲取到用户的自定義配置的。這裏考慮到我們需要對 sofa-ark-all 包進行配置,裝載進入 runtimeClasspath,以便再後續進行處理。可以通過設置 <span>afterEvaluate</span> 來解決這個問題。<span>afterEvaluate</span> 是 Gradle 提供的一個非常重要的機制,它允許我們在項目的配置階段完成之後執行相應的操作,這樣可以確保用户的自定義配置被完全考慮在內。當我們將自定義邏輯放在 <span>afterEvaluate</span> 中時,Gradle 會先完成對所有用户配置的解析和應用,包括那些在 <span>build.gradle</span> 或其他配置文件中顯式指定的屬性,然後再執行自定義的操作。

項目依賴項

在打包過程中,需要對不同 scope 的依賴進行處理,這涉及到 Gradle 的依賴管理。Gradle 中通常用兩個 Classpath 來進行管理:

  • compileClasspath​:編譯依賴項,此路徑下的依賴主要用於項目編譯階段。例如,項目使用特定的數據庫連接庫進行數據訪問層代碼的編寫,在編譯時,需要將該數據庫連接庫的相關 jar 包包含在 compileClasspath 中,這樣編譯器才能識別並正確編譯涉及數據庫操作的代碼。編譯依賴項確保了代碼在編譯期間能夠順利通過語法檢查和類型校驗,為後續的運行奠定基礎。
  • runtimeClasspath​:運行時依賴項,這些依賴是項目在實際運行過程中所必需的。以一個基於 Web 的應用為例,在運行時可能需要 Servlet 容器相關的依賴,這些依賴並不一定在編譯階段就絕對必要,但在應用啓動和處理用户請求時是不可或缺的。運行時依賴項保證了項目在運行環境中能夠正常執行各項功能。

在 Gradle 配置文件中通過 implementation、runtimeOnly 等聲明的依賴項,最終會根據不同的階段分類到上述的兩個 classpath 中。例如,使用 implementation 'com.example:library:1.0.0' 聲明的依賴,會在編譯階段添加到 compileClasspath,同時也會在運行時存在於 runtimeClasspath,因為 implementation 配置表示該依賴對於編譯和運行都至關重要。而像 runtimeOnly 'com.example:runtime-only-library:1.0.0' 聲明的依賴,則只會被添加到 runtimeClasspath,因為它僅在運行時才需要。

在 Gradle 中的 Action 中,通過獲取 runtimeClasspath 配置,可以對運行時的依賴項進行分類、篩選、過濾等操作。例如,項目在運行時可能依賴多個日誌庫,但由於某些原因,需要對日誌庫進行優化,只保留特定的日誌庫進行打包。我們便可以在 SOFAArk 打包過程中,通過自定義配置 exclude 設置依賴項的標識​(groupId、artifactId 和 version)​,去除不需要的日誌庫依賴。

啓動類和容器類

在 Ark 包的打包過程中,會對所有的依賴項進行分類,根據不同的類別,分為 Biz 包、Container 包,以及 Container 包的啓動類相關的內容。用户在使用之前,如果沒有指定相關包的配置項,會啓用默認配置,從遠程倉庫或者本地倉庫中獲取到 sofa-ark-all 內容,並在配置階段進行處理。此外,涉及到 Biz 包時,需要先將項目的所有 Biz 包打包出來後,再將這些 Biz 包寫入到 Ark 包中。

想要實現上述的這些內容,並不需要自己去另外編寫太多的代碼,Gradle 提供了 Copy 和 CopySpec 兩個工具。通過它們,我們可以極為方便地將依賴、配置項等各種內容輸出到指定的目錄。在使用這些工具時,我們只需要編寫相應的過濾和操作規則,就能夠精確地控制哪些內容需要被複制,哪些內容需要被排除,以及如何將它們按照我們期望的方式組織到目標目錄中。

//定義目標目錄為包的根目錄
this.bootInfSpec = project.copySpec().into("");
bootInfSpec.into("", 定義過濾規則));
bootInfSpec.into("lib", 定義lib下面的過濾規則));

除了 Copy 和 CopySpec,Gradle 還提供了 CopyAction 對文件進行復制操作。CopyAction 的核心功能是將文件從一個位置複製到另一個位置,這是構建系統中常見的操作,尤其是在將資源、依賴項或生成的輸出文件部署到相應的目錄時。它不僅僅是簡單的文件複製,還可以根據複製細節​(CopySpec​)來決定哪些文件需要複製,如何複製,以及複製時需要進行哪些處理。再最終將資源和內容寫入到 jar 包時的操作,便可以集中到自定義的 CopyAction 中來實現。

插件調試

初步完成插件的編寫工作後,想要在本地調試需要三步:

  • 發佈到 Maven 倉庫
  • 項目引入
  • 斷點調試

本地發佈需要在插件項目的 <span>build.gradle</span> 中設置如下:

plugins {
//    本地調試用,發佈到maven
    id 'maven-publish'
}

另外還需要對 publish 進行配置,配置完成發佈到本地倉庫後,就可以在想要測試的 Gradle 項目中進行引入,這裏一般要設置測試項目中的 <span>setting.gradle</span><span>build.gradle</span> 文件,指定插件倉庫和版本。最後,通過打斷點啓動插件,就可以對插件進行調試。

項目未來

目前,Gradle 打包依然還有一些特性亟待解決和優化,包括即將支持的 declared 模式,優化 Ark 打包過程,抽離公用代碼等內容。後續,我會進一步優化以上內容,使 Gradle 版本打包體驗完成從可用到優雅的躍遷。

開源之夏個人隨訪

自我介紹

大家好,我是​魏照華​。第一次瞭解到開源活動,是看到身邊同學參加谷歌開源之夏的經歷,當時為我推開了通往新世界的大門。我在隨後也參與了一些國內外的活動,在此過程中,逐漸感受到開源精神和開源項目對世界產生的深刻影響。

參與該項目的原因​

本次能參與 SOFAArk 打包插件項目,於我而言是份特別的緣分——三年前在社區發起的 SOFAArk 源碼解析活動中,我注意到一個關於支持 Netty 插件的 issue,當時利用閒暇時間嘗試提交了 PR,並幸運地被合併到主分支。未曾想兩年後的今天,可以再次參與 SOFAArk 項目。

如何克服項目過程中的困難與挑戰

就個人成長而言,我想分享以下兩點:技術溝通的藝術與​時間管理的哲學​。

我本身並不擅長溝通,但是在項目實施中,我愈加體會到溝通是貫穿項目全週期的生命線。從前期與導師反覆推敲項目目標、確定交付標準,到開發階段針對方案的多輪技術探討,直至後期維護方案的持續演變,每個環節都印證着一個事實——高質量的溝通本身就是技術實現的重要組成部分。這種認知讓我開始有意識地培養自己的技術表達和溝通能力。

而在時間管理維度上,由於此次項目的實施階段,我已經畢業參加工作,平衡全職工作與開源貢獻的挑戰遠超預期。特別是程序開發的工作時間往往隨着項目的進度“靈活安排”,面對職業任務與開源承諾的雙重時間需求,我不得不直面資源分配的殘酷現實:前期因在 Gradle 插件 API 上面花費了大量精力,恍然間發現時間竟已悄悄過半;中期在晝夜顛倒的編碼調試中,又不得不妥協於某些有趣構想只能置於腦海。這種“時間赤字”的困境,最終轉化為對敏捷規劃和碎片時間利用上的重新審視和思考。

有哪些感想

站在這個節點回望,非常感謝這次社區給予的機會。我會重新出發,繼續深耕開源這片土壤。​因為真正的開源精神,本就是場永不停歇的接力——我們既是前人智慧結晶的繼承者,也是未來技術圖景的腳手架搭建者​。

參考鏈接

[1]:https://www.sofastack.tech/projects/sofa-boot/sofa-ark-build-package-plugin/

user avatar u_17470194 頭像 cloudace 頭像 jisujia02 頭像 fiveyoboy 頭像 nocobase 頭像
點贊 5 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.