Java 相關(guān)工具
gradle
Overview
gradle 是一款基于JVM 的自動(dòng)化構(gòu)建工具。
gradle.build
文件是gradle 的配置文件,?其使用groovy 來(lái)構(gòu)建DSL 腳本。
在gradle 中, 有兩個(gè)基本概念:
- 項(xiàng)目是構(gòu)建產(chǎn)物(如jar 包)或?qū)嵤┊a(chǎn)物(將應(yīng)用程序部署到生成環(huán)境),一個(gè)項(xiàng)目包含若干個(gè)任務(wù)。
- 任務(wù)是指不可分割的最小工作單元,任務(wù)用來(lái)執(zhí)行真正的構(gòu)建工作。
在gradle 中,所有的特性都是由gradle 插件提供的, gradle 本身只提供了DSL 語(yǔ)言。
Usage
-
應(yīng)用gradle 插件:
apply plugin: 'java'
-
定義變量:
project.ext { guavaVersion = '18.0' ... }
-
定義依賴(lài):
project.dependencies { compile(...) runtime(...) testCompile(...) }
-
定義任務(wù):
task uberjar(type: Jar, dependsOn: jar) {...}
Tips
-
由于gradle 的各個(gè)版本間存在兼容性問(wèn)題,使用wrapper 讓每個(gè)項(xiàng)目的構(gòu)建環(huán)境獨(dú)立。
在build.gradle 文件中使用以下的配置來(lái)指定本項(xiàng)目使用的gradle 半版本信息:
task wrapper(type: Wrapper) { gradleVersion = '2.5' }
gradle的項(xiàng)目構(gòu)建過(guò)程分為三個(gè)步驟:初始化, 配置和執(zhí)行任務(wù)。使用
org.gradle.daemon=true
來(lái)啟動(dòng)后臺(tái)進(jìn)程來(lái)在構(gòu)建過(guò)程中跳過(guò)初始化步驟,從而加速gradle 的構(gòu)建執(zhí)行。-
將項(xiàng)目拆分為多個(gè)子項(xiàng)目,可以利用gradle 提供的并行構(gòu)建能力。
- 在root 項(xiàng)目中的
build.gradle
中定義公共變量,任務(wù)和依賴(lài)。 - 在root 項(xiàng)目中的
settings.gradle
中定義包含的子項(xiàng)目。 - 在各個(gè)子項(xiàng)目的
build.gradle
中定義各項(xiàng)目所需的變量,任務(wù)和依賴(lài)。
- 在root 項(xiàng)目中的
-
從cvs 中新獲取的項(xiàng)目后,在命令行中依次執(zhí)行以下的命令:
>> gradle wrapper >> ./gradlew clean idea >> ./gradlew clean check
如果
check
任務(wù)通過(guò),說(shuō)明項(xiàng)目已經(jīng)在本地成功構(gòu)建。
Nexus
Overview
nexus是一款支持多種流行組件(如Maven,Docker 等)的倉(cāng)庫(kù)管理軟件。
在nexus 中,有兩個(gè)核心的概念:
-
component:一種資源,例如
library
或者framework
。作為應(yīng)用程序的一部分,component 可以在運(yùn)行時(shí),集成,單元測(cè)試執(zhí)行,或者在構(gòu)建過(guò)程中被引用。在
build.gradle
中的表現(xiàn)為:project.dependencies { compile(...) runtime(...) testCompile(...) }
component 通常又被稱(chēng)為artifact, package, bundle, achive 等。
-
repository:為了方便components 的使用,repository 聚合了components 集合,并在internet 上提供service。
- 組件通常有不同的格式,例如maven repository 依賴(lài)于特殊的目錄結(jié)構(gòu)和XML 格式的元數(shù)據(jù), 并通過(guò)plain HTTP 命令和附帶的XML 文件來(lái)對(duì)component 進(jìn)行交互。
Usage
-
指定從自建的nexus 倉(cāng)庫(kù)中獲取依賴(lài):
buildscript { repositories { jcenter() maven { url "your-nexus-repository-url" } } dependencies { classpath 'your-dependencies' } }
-
gradle upload
命令的上傳配置:project.uploadArchives { repositories { mavenDeployer { repository(url: "your-nexus-repository-url") { authentication(userName: "", password: "") } pom.version = "${project.version}" pom.artifactId = "${project.name}" pom.groupId = "${project.group}" } } }
Tips
-
本地緩存導(dǎo)致的依賴(lài)不及時(shí)刷新新版本:
configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'hours' }
* 根據(jù)版本號(hào)來(lái)分別存儲(chǔ)snapshot 和release 組件:
mavenDeployer {
repository(url: "your-nexus-repository-url" +
"${project.version.endsWith('-SNAPSHOT') ? 'snapshots' : 'releases'}") {...
?```
checkstyle
Overview
checkstyle 是一款Java 代碼風(fēng)格的檢查工具。
它是高度可配置化的,通過(guò)配置可以支持任意的代碼風(fēng)格(例如Sun Code Convensions 和Google Java Style)。
checkstyle 的配置文件位于project/config/checkstyle/sun_checks.xml
。
通過(guò)修改該配置文件來(lái)統(tǒng)一項(xiàng)目的代碼風(fēng)格。例如,出于源碼閱讀的考慮,想要限制代碼行的長(zhǎng)度為100 個(gè)字符,則修改以下的配置:
<module name="LineLength">
<property name="max" value="100"/>
</module>
Usage
在項(xiàng)目的build.gradle
文件中,通過(guò)以下的配置使用checkstyle:
apply plugin: 'checkstyle'
checkstyle {
configFile = file("$project.projectDir/config/checkstyle/sun_checks.xml")
toolVersion = '6.7'
}
// 是否對(duì)測(cè)試代碼進(jìn)行代碼風(fēng)格的檢查。
checkstyleTest {
enabled = false
}
Tips
- 不可變形參需要加上final.
- 將數(shù)字定義為有意義的
static final
常量(否則,會(huì)被認(rèn)為是magic number)。 - 在代碼提交前, 使用IDE 提供的format 快捷鍵, 過(guò)濾掉一些低級(jí)的風(fēng)格問(wèn)題(如無(wú)用的引用, 代碼對(duì)其等等)。
findbugs
Overview
findbugs 是一款靜態(tài)檢查Java 代碼中bug 的工具。
在項(xiàng)目開(kāi)發(fā)過(guò)程中,執(zhí)行gradle check
命令時(shí),findbugs 會(huì)在coverageCheck
通過(guò)后執(zhí)行。
如果代碼中含有工具認(rèn)為可能會(huì)引發(fā)Bug 的寫(xiě)法,那么會(huì)在/yourProject/build/reports/findbugs
目錄中的生成一個(gè)main.xml
文件來(lái)記錄。
Usage
如果在check
過(guò)程中,出現(xiàn)了Execution failed for task ':findbugsMain'.
,那么打開(kāi)main.xml
文件并找到以下的節(jié)點(diǎn):
<BugInstance type="" priority="" rank="" abbrev="" category="">
根據(jù)type 字段,在網(wǎng)站中可以找到對(duì)應(yīng)的bug 的描述。
根據(jù)Class, Field, SouceLine
字段, 可以在Java 源代碼中找到bug 對(duì)應(yīng)的源碼位置。
Tips
- 方法忽略了返回值(這常見(jiàn)于對(duì)不可變對(duì)象進(jìn)行調(diào)用方法,而誤以為不可變對(duì)象會(huì)被更新)。
- 忘記資源釋放(方法可能沒(méi)有關(guān)閉stream)。
- 在
switch-case
語(yǔ)句塊中缺少break
關(guān)鍵字。
jacoco
Overview
jacoco 是一款檢查代碼單元測(cè)試覆蓋率的工具。
jacoco 的配置文件位于config/scripts/coverage.gradle
。
如果代碼的覆蓋率沒(méi)有達(dá)到指定的標(biāo)準(zhǔn),那么會(huì)在yourproject/build/report/jacoco
目錄下生成index.html
和相關(guān)的結(jié)果文件,打開(kāi)該文件,可以看到圖形化顯示的沒(méi)有覆蓋到的代碼所在的文件和代碼位置。
Usage
- 在gradle 項(xiàng)目中,在該配置文件中定義jacoco 在gradle 的擴(kuò)展和任務(wù):
project.extensions.create('coverage', CoverageExtension)
task coverageCheck(dependsOn: test) << {...}
- 在實(shí)際使用中,我們定義了
excludePackages
和excludeClasses
對(duì)象來(lái)讓jacoco 跳過(guò)一些包和類(lèi)的覆蓋率檢查。
coverage.excludePackages.each() {
exclude name: "${it.replaceAll('\\.', '/') + '/*'}"
}
coverage.excludeClasses.each() {
exclude name: "${it.replaceAll('\\.', '/') + '.class'}"
}
- 然后在
build.gradle
中使用以下的配置來(lái)在gradle check
任務(wù)中啟用jacoco 任務(wù)。
check.dependsOn "coverageCheck"
Tips
- 盡量使用TDD 的開(kāi)發(fā)方式。
- 在實(shí)際開(kāi)發(fā)過(guò)程中,除了一些無(wú)法模擬的Exception 之外,盡量不要使用exclude 來(lái)跳過(guò)單元測(cè)試覆蓋率的檢查。
flyway
Overview
flyway 是一款數(shù)據(jù)庫(kù)migration 工具。
相比于手動(dòng)的sql 腳本的數(shù)據(jù)庫(kù)數(shù)據(jù)版本管理,數(shù)據(jù)庫(kù)migration 能夠帶來(lái)以下的優(yōu)勢(shì):
- 從零開(kāi)始重建一個(gè)數(shù)據(jù)庫(kù)。
- 清晰地獲知當(dāng)期數(shù)據(jù)庫(kù)狀態(tài)。
- 從當(dāng)前版本到任意的一個(gè)新?tīng)顟B(tài)。
flyway 使用schema_version 數(shù)據(jù)表管理數(shù)據(jù)庫(kù)的數(shù)據(jù)版本。
Usage
-
在build.gradle 中進(jìn)行以下的配置:
apply plugin: "org.flywaydb.flyway" flyway { url = 'your-database-schema-url' user = '' password = '' }
在以下的位置中放置數(shù)據(jù)庫(kù)migration 腳本:
yourProject/src/main/resources/db.migration
數(shù)據(jù)庫(kù)migration 腳本的命名格式:
V1__Create_demo_table.sql
。 需要注意的是版本號(hào)后面是兩個(gè)下劃線(xiàn)。
Tips
-
在測(cè)試中自動(dòng)化地進(jìn)行
flywayClean
和flywayMigrate
:test.dependsOn "flywayClean" test.dependsOn "flywayMigrate"
- 數(shù)據(jù)schema 的更新原則是只增不刪,例如要修改某個(gè)既存表的結(jié)構(gòu),那么在一個(gè)新建的migration 腳本中進(jìn)行,而不用去修改該寄存表的創(chuàng)建腳本。