构建Java项目
在上一节中,我们确定了编写独立的待办事项应用程序所需的 Java 类。 要组装可执行程序,需要编译源代码并将类打包到 JAR 文件中。 Java 开发工具包 (JDK) 提供了 javac 和 jar 等开发工具来帮助实现这些任务。 除非您是一个受虐狂,否则您不想每次源代码更改时都手动运行这些任务。
Gradle 插件充当自动化这些任务的推动者。 插件通过引入具有合理默认值的特定于域的约定和任务来扩展您的项目。 Java 插件是 Gradle 附带的插件之一。 Java 插件远远超出了源代码编译和打包的基本功能。 它为您的项目建立标准布局,并确保任务以正确的顺序执行,以便它们在 Java 项目的上下文中有意义。 是时候为您的应用程序创建构建脚本并应用 Java 插件了。
使用Java插件
在第 1 章中,您了解到每个 Gradle 项目都是从创建名为 build.gradle 的构建脚本开始的。 创建文件并告诉您的项目使用 Java 插件,如下所示:
apply plugin: 'java'
一行代码足以构建您的 Java 代码,但是 Gradle 如何知道在哪里可以找到您的源文件呢? Java 插件引入的约定之一是源代码的位置。 默认情况下,插件在目录 src/main/java 中搜索生产源代码。 您将获取待办事项应用程序的所有类并将它们放在适当的目录下。
自动项目生成 如果您不必手动创建源目录,那不是很好吗? Maven 有一个称为项目原型的概念,这是一个从现有模板生成项目结构的插件。 不幸的是,在撰写本文时,此功能尚未成为 Gradle 核心功能。 Gradle 社区创建的插件 Gradle Templates 提出了这个问题的解决方案。 它可以在 https://github.com/townsfolk/gradle-templates 上找到。 初始化 Gradle 项目的第一次尝试是由构建设置插件自动进行的,即使没有构建脚本,您也可以使用该插件。 该插件允许生成项目文件(以及您稍后将了解的其他相关文件)。 要生成 Gradle 构建脚本,请从命令行执行 gradle setupBuild。 |
创建源文件时,请记住,用于类的包 com.manning.gia.todo 会直接转换为根源目录下的子目录。 创建构建脚本并将源代码移动到正确的位置后,您的项目结构应如下所示:
.
├── buildxxx.gradle
└── src
└── main
└── java
└── com
└── manning
└── gia
└── todo
├── ToDoApp.java
├── model
│ └── ToDoItem.java
├── repository
│ ├── InMemoryToDoRepository.java
│ └── ToDoRepository.java
└── utils
├── CommandLineInput.java
└── CommandLineInputHandler.java
构建项目
您已准备好构建该项目。 Java 插件添加到项目中的任务之一名为构建。 构建任务会编译您的代码、运行您的测试并组装 JAR 文件,所有这些都按正确的顺序进行。 运行命令 gradle build 应该会给出与此类似的输出:
$ 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 插件提供的一个已执行任务。 您可能会注意到,某些任务标有 “UP-TO-DATE” 消息。 这意味着任务被跳过。 Gradle 的增量构建支持自动识别不需要完成任何工作。 特别是在大型企业项目中,此功能被证明确实可以节省时间。 在第 4 章中,您将学习如何将这个概念应用到您自己的任务中。 在命令行输出中,您可以看到跳过任务的具体示例:compileTestJava 和 testClasses。 当您在默认目录 src/test/java 中提供任何单元测试时,Gradle 会愉快地继续前进。 如果您想了解如何为应用程序编写测试并将其集成到构建中,请参阅第 7 章。以下是执行构建后的项目结构:
.
├── build
│ ├── classes
│ │ └── main
│ │ └── com
│ │ └── manning
│ │ └── gia
│ │ └── todo
│ │ ├── ToDoApp.class
│ │ ├── model
│ │ │ └── ToDoItem.class
│ │ ├── repository
│ │ │ ├── InMemoryToDoRepository.class
│ │ │ └── ToDoRepository.class
│ │ └── utils
│ │ ├── CommandLineInput.class
│ │ ├── CommandLineInputHandler$1.class
│ │ └── CommandLineInputHandler.class
│ ├── dependency-cache
│ ├── libs
│ │ └── todo-app.jar
│ ├── reports
│ │ └── tests
│ │ ├── base-style.css
│ │ ├── css3-pie-1.0beta3.htc
│ │ ├── index.html
│ │ ├── report.js
│ │ └── style.css
│ ├── test-results
│ └── tmp
│ └── jar
│ └── MANIFEST.MF
├── buildxxx.gradle
└── src
在项目的根级别上,您现在还会找到一个名为 build 的目录,其中包含构建运行的所有输出,包括类文件、测试报告、组装的 JAR 文件以及归档所需的临时文件(例如清单) 。 如果您以前使用过使用标准输出目录目标的构建工具 Maven,那么该结构应该看起来很熟悉。 构建输出目录的名称是所有 Gradle 构建通用的可配置标准属性。 您已经看到按照惯例构建 Java 项目是多么轻松,无需您进行任何额外配置。 JAR 文件在 build/libs 下创建并准备好执行。 重要的是要了解 JAR 文件的名称源自项目名称。 只要您不重新配置它,就会使用您项目的目录名称,在本例中为 todo-app。 让我们看看待办事项应用程序的运行情况。
运行项目
运行 Java 程序很容易。 现在,您只需从项目的根目录使用 JDK 的 java 命令:
$ java -cp build/classes/java/main com.manning.gia.todo.ToDoApp
--- To Do Application ---
Please make a choice:
(a)ll items
(f)ind a specific item
(i)nsert a new item
(u)pdate an existing item
(d)elete an existing item
(e)xit
>
Java 程序启动,打印所有可用待办事项的列表,并等待您在命令提示符下输入。
Java 独立应用程序支持 Gradle 可以进一步简化独立 Java 应用程序的构建。 另一个值得一提的标准 Gradle 扩展是应用程序插件。 该插件提供了用于简化运行和捆绑应用程序的行为的任务。 |
就是这样——您毫不费力地实现了一个 Java 应用程序并使用 Gradle 构建了它。 只要您遵守标准约定,所需要的只是构建脚本中的一行字。 接下来,我们将了解如何自定义构建约定标准。
定制你的项目
Java 插件是一个有主见的小型框架。它为项目的许多方面(如布局)假设了合理的默认值。如果你对世界的看法不同,Gradle 还提供了自定义约定的选项。如何知道哪些是可配置的?Gradle 的《构建语言参考》(Build Language Reference)就是一个很好的开始,请访问 http://www.gradle.org/docs/current/dsl/ 。还记得第 2 章中的命令行选项 properties 吗?运行 gradle properties,你会看到一个可配置的标准属性和插件属性列表,以及它们的默认值。你将通过扩展初始构建脚本来定制项目。
修改项目和插件属性
在以下示例中,您将为项目指定版本号并指示 Java 源兼容性。 之前,您使用 java 命令运行待办事项应用程序。 您通过 -cp build/classes/main 将构建输出目录分配给类路径命令行选项,告诉 Java 运行时在哪里可以找到类。 为了能够从 JAR 文件启动应用程序,清单 MANIFEST.MF 需要包含标头 Main-Class。 以下清单演示了如何在构建脚本中配置默认值并向 JAR 清单添加标头属性。
version = 0.1
sourceCompatibility = 1.6
jar {
manifest {
attributes 'Main-Class': 'com.manning.gia.todo.ToDoApp'
}
}
组装 JAR 文件后,您会发现版本号已添加到 JAR 文件名中。文件名由 todo-app.jar改为 todo-app-0.1.jar。现在,生成的 JAR 文件包含了主类头文件,您可以使用 java -jar build/libs/todo-app-0.1.jar 运行应用程序。接下来,我们将了解如何将项目结构改造为传统布局。
改造遗留项目
企业在启动新的软件项目时很少会一帆风顺。很多时候,你必须与遗留系统集成,迁移现有项目的技术栈,或者遵守内部标准或限制。构建工具必须足够灵活,以便通过配置默认设置来适应外部限制。
在本节中,我们将举例说明 To Do 应用程序的可定制性。假设你在启动项目时使用了不同的目录布局。您没有将源代码文件放入 src/main/java,而是选择了使用 src 目录。如果你想更改默认的测试源代码目录,同样的概念也适用。此外,你还想让 Gradle 将构建输出渲染到 out 目录,而不是标准值构建。下一个列表展示了如何使构建适应自定义的项目布局。
sourceSets {
main {
java {
srcDirs = ['src'] // 用不同目录的列表替换传统的源代码目录
}
}
test {
java {
srcDirs = ['test'] // 用不同目录的列表替换传统的测试源代码目录
}
}
}
buildDir = 'out' // 将项目输出属性更改为目录输出
定制构建的关键是了解底层属性和 DSL 元素。 接下来,我们将了解如何使用外部库的功能。
配置和使用外部依赖
让我们回想一下 ToDoApp 类中的 main 方法。 您编写了一些代码来从控制台读取用户的输入,并将第一个字符转换为待办事项命令。 为此,您需要确保输入的输入字符串的长度仅为一位数字。 否则,您将分配 Unicode 空字符:
String input = commandLineInputHandler.readInput();
char[] inputChars = input.length() == 1 ? input.toCharArray() : new char[] { DEFAULT_INPUT };
command = inputChars[0];
我打赌您可以通过重用包装此逻辑的库来改进此实现。 完美的匹配是 Apache Commons Lang 库中的 CharUtils 类。 它提供了一个名为 toChar 的方法,该方法仅使用第一个字符将 String 转换为 char,如果字符串的值为空,则使用默认字符。 以下代码片段显示了输入解析代码的改进版本:
import org.apache.commons.lang3.CharUtils;
String input = commandLineInputHandler.readInput();
command = CharUtils.toChar(input, DEFAULT_INPUT);
那么如何告诉 Gradle 引用 Apache Commons Lang 库呢? 我们将研究两个 DSL 配置元素:存储库和依赖项。
定义存储库
在 Java 世界中,依赖项以JAR文件的形式分发和使用。 许多库都可以在存储库中使用,例如文件系统或中央服务器。 Gradle 要求您至少定义一个存储库才能使用依赖项。 出于您的目的,您将使用公开可用、可通过 Internet 访问的存储库 Maven Central:
repositories {
mavenCentral() // 配置 Maven Central 2 存储库的快捷方式可在 http://repo1.maven.org/maven2 下访问
}
存储库就位后,您就可以声明该库了。 让我们看看依赖本身的定义。
定义依赖关系
依赖关系是通过组标识符、名称和特定版本来定义的。 您将使用该库的 3.1 版本,如以下代码片段所示:
dependencies {
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1'
}
在 Gradle 中,依赖项按配置进行分组。 Java 插件引入的配置之一是 compile。 您可能可以通过配置名称看出它用于编译源代码所需的依赖项。
如何找到依赖项 查找有关 Maven Central 依赖项的详细信息非常简单。 该存储库为您提供了一个易于使用的搜索界面:http://search.maven.org/。 |
解析依赖
Gradle 会自动检测项目中的新依赖项。 如果依赖关系尚未成功解决,它将通过下一个需要其正常工作的任务调用来下载它 - 在本例中为任务 compileJava:
$ gradle build
:compileJava
Download http://repo1.maven.org/maven2/org/apache/commons/
➥ commons-lang3/3.1/commons-lang3-3.1.pom
Download http://repo1.maven.org/maven2/org/apache/commons/
➥ commons-parent/22/commons-parent-22.pom
Download http://repo1.maven.org/maven2/org/apache/apache/9/
➥ apache-9.pom
Download http://repo1.maven.org/maven2/org/apache/commons/
➥ commons-lang3/3.1/commons-lang3-3.1.jar
:processResources UP-TO-DATE
...
:build
第 5 章将更深入地讨论依赖管理主题。 我知道当前形式的 “待办事项” 应用程序不会让您大吃一惊。 是时候通过添加具有视觉吸引力的用户界面来对其进行现代化改造了。
