使用Gradle构建Java工程

  这篇博客会带你一起来使用Gradle构建Java工程。

创建一个Java工程

  首先,新建一个Java工程,用于演示如何使用Gradle来构建它。为了让我们的精力更专注于如何使用Gralde,我们将这个工程建得非常简单。

  创建一个工程根目录,然后在Terminal打开它。

$ mkdir gs-gradle
$ cd gs-gradle

  使用命令mkdir -p src/main/java/hello创建工程目录结构。你会得到以下的目录。

└── src
    └── main
        └── java
            └── hello

  在src/main/java/hello目录下,你可以创建任意你想要的Java类。为了方便接下来的演示,这里仅创建两个简单的类:HelloWorld.javaGreeter.java

src/main/java/hello/HelloWorld.java

package hello;

public class HelloWorld {
  public static void main(String[] args) {
    Greeter greeter = new Greeter();
    System.out.println(greeter.sayHello());
  }
}

src/main/java/hello/Greeter.java

package hello;

public class Greeter {
  public String sayHello() {
    return "Hello world!";
  }
}

安装Gradle到你的工程

  根据上一篇博客配置Gradle环境。如果你已经完成配置环境了,那可以接着做以下的步骤。

  回到项目根目录,在Terminal输入gradle。如果以上步骤都没有错的话,你会得到以下信息。

$ gradle
:help

Welcome to Gradle 2.14.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 1.428 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.14/userguide/gradle_daemon.html

  现在,你的项目可以使用Gradle来构建了。

查看Gradle可以做什么

  现在,Gradle已经安装好了,那么来看看Gradle可以做些什么。无论你有没有创建build.gradle文件,你都可以使用以下命令查看Gradle有哪些任务是可用的。

bradle tasks

  可以看到,Gralde列出了一系列可用的任务。由于之前创建的工程中,没有build.gradle文件,因此,你应该会得到以下信息。

$ gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'gs-gradle'.
components - Displays the components produced by root project 'gs-gradle'. [incubating]
dependencies - Displays all dependencies declared in root project 'gs-gradle'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gs-gradle'.
help - Displays a help message.
model - Displays the configuration model of root project 'gs-gradle'. [incubating]
projects - Displays the sub-projects of root project 'gs-gradle'.
properties - Displays the properties of root project 'gs-gradle'.
tasks - Displays the tasks runnable from root project 'gs-gradle'.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 1.122 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.14/userguide/gradle_daemon.html

  以上列出来的任务都是可用的。但是,使用gradle.build可以使任务变得更有用处。可用任务列表会随着你添加插件到build.gradle文件而变得更多。

Build Java code

  在项目根目下,创建一个最简单的build.gradle文件,文件只有以下一行内容:

apply plugin: 'java'

  这简单的一行配置为工程带来很大的改变。再运行一次gradle tasks命令,可以看到任务列表多出了很多项,包括buildjavadoc等。

  你会经常使用到gradle build。这项任务会编译、测试,并且会将代码组装成JAR文件。

gradle build

  经过几秒时间的运行,BUILD SUCCESSFUL代表着build任务已经完成了。

  我们来看看,这次任务的运行结果。打开项目根目录,可以看到,多了一个build文件夹。build文件夹下面,将会包括以下这几个文件夹。

  • classes. 项目的编译后的.class文件
  • reports. 编译产生的报告(例如测试报告)
  • libs. 项目编译后的包(通常是jar或war包)

  classes文件夹下保存编译后的Java代码。你可以在这里找到HelloWorld.classGreeter.class

  由于这个项目没有依赖任何其它库,所以dependency_cahce文件夹下面是空的。

  libs文件夹下应该有一个与项目同名的JAR文件。将来,你可以自己指定JAR包的文件和版本号。

声明依赖

  刚才那个Hello World项目是一个非常简单的项目,没有依赖任何外部库。但是,大多数应用会依赖外部库来处理命令和复杂的功能。

  继续刚才的Hello World项目。假设你现在需要让你的应用打印当前的日期和时间。当然,你可以使用Java原生的日期时间库来完成这个功能,但是,如果你使用Joda Time库来完成这个功能的话,会更有意思。

  首先,将HelloWorld.java文件修改成以下的样子。

package hello;

import org.joda.time.LocalTime;

public class HelloWorld {
  public static void main(String[] args) {
    LocalTime currentTime = new LocalTime();
    System.out.println("The current local time is: " + currentTime);

    Greeter greeter = new Greeter();
    System.out.println(greeter.sayHello());
  }
}

  现在,HelloWorld使用Joda TimeLocalTime类来获取以及显示当前的时间。

  如果你现在使用gradle build来构建项目,这个任务将会失败,因为你还没有声明项目需要依赖Joda Time库。

  你现在需要为第三方库添加源。在build.gradle中添加以下内容。

repositories {
    mavenCentral()
}

  repositories块指定了构建应该使用Maven中央仓库来解决项目的依赖。Gradle依靠许多约定和Maven已建立好的设施,包括使用Maven的中央库来作为库依赖源。

  现在,我们来声明第三方依赖。在build.gradle中添加以下内容。

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile "joda-time:joda-time:2.2"
}

  在dependencies块里,我们为项目声明了一个依赖Joda Time。并且,你的请求指定了去获取joda-time分组下的版本为2.2的joda-time库(以:分隔,第一个是分组,第二个是库名,第三个是版本号)。

  另外一个需要注意的是,这个依赖是一个compile依赖,这指明了它在编译时(compile-time)应该是可用的(如果你是在编译WAR包,那么这个库应该被包含在/WEB-INF/libs目录下)。除了compile依赖,还存在以下两种依赖:

  • providedCompile。仅在编译时依赖,这个库会在容器运行时提供(比如Java Servlet API)。
  • testCompile。仅在运行测试用例时依赖,在打包或运行项目时不需要依赖。

  最后,我们来指定生成的JAR包的名字。在build.gradle中添加以下内容。

jar {
    baseName = 'gs-gradle'
    version =  '0.1.0'
}

  jar块里指定了JAR文件会如何被命名。在这个例子里,它最终会变成gs-gradle-0.1.0.jar

  现在,你再来运行gradle build命令,Gradle已经可以在Maven中央库里解决Joda Time的依赖问题,并编译成功。在build/libs文件夹里,看看是否存在gs-gradle-0.1.0.jar文件?

使用Gradle Wrapper来构建项目

  Gradle Wrapper是Gradle推荐的打包方式。它包含了Windows批处理脚本、OS X/Linux的Shell脚本。这些脚本允许你在没有安装Gradle环境下使用Gradle打包你的项目。在build.gradle下添加以下内容。

task wrapper(type: Wrapper) {
    gradleVersion = '2.14'
}

  添加后,使用gradle wrapper命令生效。它会去下载并初始化wrapper脚本。命令完成之后,你会发现根目录下多了几个文件。

└── gradlew
└── gradlew.bat
└── gradle
    └── wrapper
        └── gradle-wrapper.jar
        └── gradle-wrapper.properties

  现在,Gradle Wrapper已经准备好构建项目了。把wrapper添加到你的版本控制系统,那么其他人clone你的项目下来之后,就可以以相同方式、相同的Gradle版本来构建项目了。和之前使用gradle命令相似,运行wrapper脚本来执行任务构建任务:

./gradlew build

  当你第一次运行指定版本的Gradle wrapper,它会去下载并缓存该版本的Gradle的安装文件。Gradle Wrapper设计之初,就是为了让它能够被提交到版本控制系统中,任何人在没有配置该版本的Gradle都可以正常构建项目。

  现在,你应该可以构建你的代码了。

build
├── classes
│   └── main
│       └── hello
│           ├── Greeter.class
│           └── HelloWorld.class
├── dependency-cache
├── libs
│   └── gs-gradle-0.1.0.jar
└── tmp
    └── jar
        └── MANIFEST.MF

  来看看gs-gradle-0.1.0.jar里面的内容:

$ jar tvf build/libs/gs-gradle-0.1.0.jar
  0 Mon Jun 27 05:32:34 CST 2016 META-INF/
 25 Mon Jun 27 05:32:34 CST 2016 META-INF/MANIFEST.MF
  0 Mon Jun 27 05:32:34 CST 2016 hello/
369 Mon Jun 27 05:32:34 CST 2016 hello/Greeter.class
988 Mon Jun 27 05:32:34 CST 2016 hello/HelloWorld.class

  我们可以看到,class文件被打包进来了。但是我们注意到,它并不能运行,即使你声明了你需要依赖joda-time,但是库并没有被打包到这个JAR文件中。

  如果需要让这个JAR变得可运行,我们需要借助于gradle的application插件。把以下内容添加到build.gradle文件中。

apply plugin: 'application'

mainClassName = 'hello.HelloWorld'

  现在,你可以运行应用了!

$ ./gradlew run
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run
The current local time is: 05:39:55.523
Hello world!

BUILD SUCCESSFUL

Total time: 2.327 secs

This build could be faster, please consider using the Gradle Daemon: https://docs.gradle.org/2.14/userguide/gradle_daemon.html

  依赖打包有不同的方式。举个例子,我们要构建一个WAR包,可以使用gradle的WAR插件来将第3方依赖打包在一起。

  此时,你的build.gradle文件应该是这样的。

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'

mainClassName = 'hello.HelloWorld'

// tag::repositories[]
repositories {
    mavenCentral()
}
// end::repositories[]

// tag::jar[]
jar {
    baseName = 'gs-gradle'
    version =  '0.1.0'
}
// end::jar[]

// tag::dependencies[]
sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile "joda-time:joda-time:2.2"
}
// end::dependencies[]

// tag::wrapper[]
task wrapper(type: Wrapper) {
    gradleVersion = '2.3'
}
// end::wrapper[]

总结

  恭喜你,你现在已经可以使用Gradle build文件来简单、高效地构建Java项目了。

Alan Yeh

简单技术控,喜欢接触各类技术

GZ, China http://yerl.cn