学习 Gradle —— Java 下一代构建工具(一)

高效软件工程的核心:自动化
自动化构建类型及其进化史:请求式构建 -> 触发式构建 -> 计划式构建
自动化构建工具要素 :任务(DAG - Directed Acyclic Graph,即一段可工作的构件单元 + 依赖)、依赖管理

Gradle 箴言

  • 让不可行变得可行,让可行变得易行,让易行变得简洁
  • Maven VS Gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
apply plugin: 'java'
group = 'com.mycompany.app'
archivesBaseName = 'my-app'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:4.11'
}

Gradle API

与其它构建构建集成

  • Gradle Script -> import -> Ant Scripts
  • Gradle Script -> use -> Ant tasks
  • Maven POM -> generate via converter -> Gradle Script
  • Gradle Script -> publish artifact -> Maven/Ivy repository -> download dependency -> Gradle Script

持续交付

  1. 提交阶段 :编译和单元测试 -> 集成测试 -> 代码评审 -> 构件
  2. 可行阶段 ;部署 -> 烟雾及可行性测试
  3. UAT 阶段 :即用户验收,部署 -> 烟雾测试
  4. PRD 阶段 :即运行上线,部署 -> 烟雾测试

安装 Gradle

  1. 安装 Java
  2. 下载 Gradle
  3. 配置 :export GRADLE_HOME=/opt/gradle 和 export PATH=$PATH:$GRADLE_HOME/bin
  4. 验证 :$ gradle -v
  5. 其它可选配置 :GRADLE_OPTS=”-Xmx1024m”,更改 JVM 堆大小

一个简单的例子

1
2
3
4
5
task helloWorld {
doLast {
println 'Hello world!'
}
}
1
2
$ gradle –q helloWorld
Hello world!

一个简单依赖的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
task startSession << {
chant()
}
def chant() {
ant.echo(message: 'Repeat after me...')
}
3.times {
task "yayGradle$it" << {
println 'Gradle rocks'
}
}
yayGradle0.dependsOn startSession
yayGradle2.dependsOn yayGradle1, yayGradle0
task groupTherapy(dependsOn: yayGradle2)
// startSession -> yayGradle0 -> yayGradle1 -> yayGradle2 -> groupTherapy
1
2
3
4
5
6
7
8
9
10
$ gradle groupTherapy
:startSession
[ant:echo] Repeat after me...
:yayGradle0
Gradle rocks
:yayGradle1
Gradle rocks
:yayGradle2
Gradle rocks
:groupTherapy

更多命令

1
2
3
4
$ gradle -q tasks # 列出任务
$ gradle -q tasks --all # 列出详细任务
$ gradle yayGradle0 groupTherapy # 执行某一任务
$ gradle groupTherapy -x yayGradle0 # 排除某一任务,如 yayGradle0

案例(一):ToDo App 工程

  • 默认情况下,java 插件会在 src/main/java 中搜索源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
├── build.gradle
└── src
└── main
└── java
└── com
└── manning
└── gia
└── todo
├── ToDoApp.java
├── model
│ └── ToDoItem.java
├── repository
│ ├── InMemoryToDoRepository.java
│ └── ToDoRepository.java
└── utils
│ ├── CommandLineInput.java
│ └── CommandLineInputHandler.java
├── build
│ ├── libs
│ │ └── todo-app.jar
  • build.gradle
1
apply plugin: 'java'
1
2
3
4
5
6
7
8
9
10
11
12
13
$ gradle build
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
:check
:build
$ java -cp build/classes/main com.manning.gia.todo.ToDoApp # running the project
  • use below : $ java –jar build/libs/todo-app-0.1.jar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apply plugin: 'java'
version = 0.1
sourceCompatibility = 1.6
sourceSets {
main {
java {
srcDirs = ['src']
}
}
test {
java {
srcDirs = ['test']
}
}
}
buildDir = 'out' // Changes project output property to directory out
jar {
manifest {
attributes 'Main-Class': 'com.manning.gia.todo.ToDoApp'
}
}

配置和使用外部依赖

1
2
3
4
5
6
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1'
}

案例(二):ToDo Web App 工程

1
2
3
4
5
6
7
8
9
10
11
12
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'
repositories {
mavenCentral()
}
dependencies {
providedCompile 'javax.servlet:servlet-api:2.5',
'javax.servlet.jsp:jsp-api:2.1'
runtime 'javax.servlet:jstl:1.1.2',
'taglibs:standard:1.1.2'
}
1
2
3
4
5
6
7
8
9
├── build.gradle
└── src
└── main
├── java
└── webapp
├── WEB-INF
│ └── web.xml
├── css
└── jsp
  • 运行 $ jar tf todo-webapp-0.1.war,其 WAR 包目录结构如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
├── META-INF
│ └── MANIFEST.MF
├── WEB-INF
│ ├── classes
│ │ └── com
│ │ └── manning
│ │ └── gia
│ │ └── todo
│ │ ├── model
│ │ │ └── ToDoItem.class
│ │ ├── repository
│ │ │ ├── InMemoryToDoRepository.class
│ │ │ └── ToDoRepository.class
│ │ └── web
│ │ └── ToDoServlet.class
│ ├── lib
│ │ └── jstl-1.1.2.jar
│ └── web.xml
├── css
│ ├── base.css
│ └── bg.png
└── jsp
├── index.jsp
└── todo-list.jsp

定制工程布局(unconventional web project layouts)

  • 在置入的容器中运行 :$ gradle jettyRun
1
2
3
4
5
6
7
8
9
10
11
12
13
├── build.gradle
├── src
│ └── main
│ └── java
│ └── ...
├── static
│ └── css
└── webfiles
├── WEB-INF
│ └── web.xml
└── jsp
├── index.jsp
└── todo-list.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'
repositories {
mavenCentral()
}
dependencies {
providedCompile 'javax.servlet:servlet-api:2.5',
runtime 'javax.servlet:jstl:1.1.2',
}
webAppDirName = 'webfiles'
war {
from 'static'
}

定制 jetty : http://localhost:9090/todo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'
repositories {
mavenCentral()
}
dependencies {
providedCompile 'javax.servlet:servlet-api:2.5',
runtime 'javax.servlet:jstl:1.1.2',
}
jettyRun {
httpPort = 9090
contextPath = 'todo'
}

Gradle wrapper

  • 包装器 :运行 Gradle 脚本而无需事先安装 Gradle,同时还能选择 Gradle 版本。
  • 运行 :$ gradle wrapper 或 $ gradlew jettyRun
1
2
3
4
5
6
7
8
9
10
11
12
13
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'
repositories {
mavenCentral()
}
dependencies {
providedCompile 'javax.servlet:servlet-api:2.5',
runtime 'javax.servlet:jstl:1.1.2',
}
task wrapper(type: Wrapper) {
gradleVersion = '1.7'
}
  • 运行后其目录结构如下 :
1
2
3
4
5
6
7
8
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
└── src

定制 Wrapper

1
2
3
4
5
task wrapper(type: Wrapper) {
gradleVersion = '1.2'
distributionUrl = 'http://myenterprise.com/gradle/dists' // 获取 Wrapper 的链接
distributionPath = 'gradle-dists' // 解压 Wrapper 存放的目录
}