網站首頁 編程語言 正文
腳手架
腳手架是為了保證各施工過程順利進行而搭設的工作平臺
而在程序開發過程中,每個工程或者說公司也都需要一個腳手架工具。通過腳手架命令行的形式簡化開發流程,避免發生一些人為的相對低級的問題,所以這個也就是為什么叫做腳手架的原因吧。
而由于每個公司的代碼規范都不同,一般情況下會主動讓開發同學進行工程方面的cv操作,就是成本高并且容易出錯。這也就是為什么我們打算寫一些這樣的工具的原因。
在一般情況下,更多的程序猿會選擇用python去寫,因為腳本語言的靈活性,但是對于一個辣雞安卓來說會增加額外的學習成本,所以這就取決于有沒有天賦了,能不能對一門陌生的語言快速上手了。
這次文章會介紹的是用kotlin去構建一個二進制文件,通過這個來完成腳手架cli工具的建設。
開搞
demo 工程地址?TheNext
一開始的啟發在于有時候使用一些第三方工具的時候會提供一個jar包,然后只要輸入java -jar xxx.jar
就可以使用這個jar包中的Main函數了。
因為是一個jar包,所以里面的內容肯定也都是用jvm內的幾種語言來進行編寫的,那么這就讓我們這種老年選手看到了一絲絲的希望。
開發調試
先建立了一個java工程,然后構建了一個main函數,之后開始進行代碼編寫。但是如果每次都需要先打包之后在通過java -jar
來執行的話非常不便利開發并且debug。而且模擬入參也灰常的惡心,你也知道的程序猿都是懶人嗎。
所以我們就借用了unittest
的能力,對于入參進行mock進行簡單的調試功能了。
參考地址?github.com/Leifzhang/T…
class Sample { @Test fun help() { Next.main( arrayOf( "--help" ) ) } @Test fun testAndroidModule() { val file = File("") val moduleName = "strike-freedom" val groupName = "com.kronos.common" Next.main( arrayOf( "module", "android", "-file", file.absolutePath, "-name", moduleName, "-group", groupName ) ) } @Test fun testAndroidApplication() { val file = File("../app/") val projectName = "freedom" Next.main( arrayOf( "project", "android", "-name", projectName, "-file", file.absolutePath ) ) } }
此處我們將Main函數通過unittest來進行模擬,這樣就可以方便我們在開發階段快速調試腳手架的能力了。
每個方法塊都可以認為是一個運行的入口,通過這個來模擬出程序所需要的入參。從而一邊完成了測試代碼的編寫,一邊完成了調試入口。
jcommander
這是一個讓我們可以更像模像樣的寫一個cli的入參解析工具,即使參數順序是錯亂的,我們仍然能解析出我們想要的數據結構,讓我們的工程看起來更正規一點。而且這個庫也被很多開源項目所使用,基本算的上是千錘百煉了,比如美團的walle
。
jcommander值得你一個star的
@Parameters(commandDescription = "args 參數") class CommandEntity { @Parameter( names = ["-file", "-f"], required = true, converter = FileConverter::class, description = "生成目標文件路徑" ) lateinit var file: File @Parameter( names = ["-name"], required = true, description = "文件名" ) lateinit var name: String @Parameter(names = ["-group", "-bundle", "-g", "-b"], description = "唯一標識符") var group: String? = null }
override fun handle(args: Array<String>) { val commandEntity = CommandEntity() JCommander.newBuilder().addObject(commandEntity).build().parse(*args) }
實例demo如上,我也是參考了官方demo寫的。通過JCommander
將args解析成對應的數據實體結構。
Main 函數聲明
我們要在build.gradle
內的jar的task中,聲明當前jar的main
函數,作為命令行工具的入口。否則打出來的jar包就會報沒有main函數的異常。
jar { exclude("**/module-info.class") /* from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }*/ manifest { attributes 'Main-Class': 'com.kronos.mebium.Next' } }
其中from的含義就是將一個jar包把所有的依賴都打到一起,從而形成一個fatjar
,而后續因為使用了gradle提供的application
插件,所以這行被我注釋了。
壓縮模板
我們這個腳手架最核心的就是把一部分工程模板壓縮成一個zip資源文件,打包帶入jar產物中。然后呢我這個人又比較懶,希望每次執行打包的時候都進行一次模板的壓縮替換,所以這里我通過一部分gradle task
來進行執行了。
abstract class ZipTask extends DefaultTask { @InputDirectory Provider<File> library = project.objects.property(File) @OutputFile Provider<File> outputFile = project.objects.property(File) @TaskAction def doAction() { def outputFile = outputFile.get() createFileSafety(outputFile) compress(library.get(), outputFile) } static File compress(final File srcDir, final File zipFile) { ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile)) srcDir.eachFileRecurse({ zos.putNextEntry(new ZipEntry(it.path - srcDir.path + (it.directory ? "/" : ""))) if (it.file) { zos << it.bytes } zos.closeEntry() }) zos.close() return zipFile } private static File createFileSafety(File file) { if (file.exists()) { file.delete() } if (!file.getParentFile().exists()) { file.getParentFile().mkdirs() } return file } }
首先定義出一個task,然后定義好輸入輸出,輸入的是一個文件夾,輸出的則是一個zip的壓縮文件,輸入輸出的地址由外部來聲明。
def moduleTask = project.tasks.register("zipAndroidLib", ZipTask.class) { it.library.set(file("../library")) it.outputFile.set(file("./src/main/resources/zip/android/android.zip")) } def projectTask = project.tasks.register("zipAndroidProject", ZipTask.class) { it.library.set(file("../project")) it.outputFile.set(file("./src/main/resources/zip/android/project.zip")) } afterEvaluate { project.tasks.findByName("compileJava").dependsOn(moduleTask) project.tasks.findByName("compileJava").dependsOn(projectTask) }
然后直接聲明處兩個task,之后把compileJava
依賴到這兩個task上去,這樣就可以保證每次compileJava
,這兩個task都會被執行到了。編譯緩存我就不說了,大家自行領悟吧。
java resource 讀取方式 ?javaClass.classLoader.getResourceAsStream(name) 就可以了。
放飛自我
接下來我們就可以在命令行工具內放飛自我,開始很簡單的通過unittest來進行代碼的編寫和調試了。
我們就可以通過自己熟悉的kotlin或者java來編寫一個簡單的cli工具,從而來進一步的做到基于工程定制化的一些方便的腳手架工具了。
生成最終產物
這里我們使用了 gradle提供的application plugin
,這個插件可以將java jar包裝成一個可執行文件的zip的壓縮包。格式如下圖所示:
而這個的生成指令就是,通過./gradlew impact:assembleDist 任務生成對應的二進制壓縮包。
這樣的好處就是我們可以省略掉java -jar xxxxx.jar
的繁瑣操作,通過可執行文件直接達到我們寫一個cli的便利。
結尾
工程內的代碼還是比較簡單的,有興趣的就自己讀一下,只是一個demo而已。
還是那句因為菜,不想去學一門新語言。如果萬一哪怕我的py在強那么一點點,我也考慮用py來寫了,哈哈哈哈哈。
原文鏈接:https://juejin.cn/post/7171248904971747341
相關推薦
- 2022-02-27 記錄npm 在windows下cmd中報錯以及idea下terminal安裝依賴時同樣報錯
- 2022-07-18 SpringMVC文件上傳功能實現
- 2022-05-06 pycharm使用sftp同步服務器的步驟_python
- 2023-12-15 Linux系統——退出vi編輯模式
- 2022-06-27 教你用Python按順序讀取文件夾中文件_python
- 2022-02-07 出現報錯nginx: [emerg] unknown directive nginx.htacces
- 2022-06-28 Python技法之如何用re模塊實現簡易tokenizer_python
- 2022-06-15 python多線程實現動態圖繪制_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支