读书笔记《gradle-effective-implementations-guide-second-edition》多项目构建
让我们从一个简单的多项目结构开始。我们有一个名为 garden
的根项目以及另外两个项目, tree
和 flower
。项目结构如下:
└── garden ├── flower └── tree
我们学习如何在多项目构建中调用任务,如下所示:
我们将为每个项目添加一个新的
printInfo
任务。该任务会将项目名称打印到System.out
。我们必须为每个项目添加一个build.gradle
文件,内容如下:task printInfo << { println "This is ${project.name}" }
要为每个项目执行任务,我们必须首先进入正确的目录,然后使用 Gradle 调用任务。我们还可以使用 Gradle 的
-b
参数为特定项目运行build.gradle
。如果我们为每个项目运行printInfo
任务,我们将得到以下输出:garden $ gradle -q printInfo This is garden garden $ cd tree tree $ gradle -q printInfo This tree tree $ cd .. garden $ gradle -b flower/build.gradle -q printInfo This is flower garden $
我们有多个项目,但我们还没有使用 Gradle 对多项目构建的支持。
让我们重新配置我们的项目并使用 Gradle 的多项目支持。我们需要在
garden
目录中添加一个新文件settings.gradle
。在这个文件中,我们将定义属于我们的多项目构建的项目。我们使用include()
方法来设置作为我们多项目构建一部分的项目。具有settings.gradle
文件的项目自动成为构建的一部分。我们将在settings.gradle
文件中使用以下行来定义我们的多项目构建:include('tree', 'flower')
现在,我们可以使用单个命令为每个项目执行
printInfo
任务。如果我们执行任务,我们将得到以下输出:garden $ gradle printInfo :printInfo This is garden :flower:printInfo This is flower :tree:printInfo This is tree BUILD SUCCESSFUL Total time: 0.684 secs
我们看到每次调用 printInfo
任务的输出。 project
任务的路径也会显示出来。根项目用冒号 (:
) 表示,没有明确的名称。 flower
项目被引用为 :flower
,而 printInfo
flower
项目的任务被引用为 :flower:printInfo
。任务的路径是项目的名称,用冒号 (:
) 后跟任务名称。冒号分隔项目和任务名称。我们还可以使用命令行中的这种语法来引用项目中的特定任务。如果我们要调用 flower
项目的 printInfo
任务,可以运行如下命令:
garde $ gradle :flower:printInfo
:flower:printInfo
This is flower
BUILD SUCCESSFUL
Total time: 0.649 secs
这也适用于从另一个项目目录执行根项目中的任务。如果我们先进入flower
项目目录,想要执行根项目的 printInfo
任务,必须使用 ;:printInfo
语法。如果我们从 执行根项目、当前项目和 flower
项目的 printInfo
任务,我们得到以下输出;tree
项目目录:
tree $ gradle :printInfo printInfo :flower:printInfo
:printInfo
This is garden
:tree:printInfo
This is tree
:flower:printInfo
This is flower
BUILD SUCCESSFUL
Total time: 0.632 secs
Gradle 需要几个步骤来确定一个项目是必须作为单项目构建还是多项目构建执行,如下所示:
首先,Gradle 在与当前目录同级的目录中寻找一个
settings.gradle
文件master
目录。如果
settings.gradle
未找到,则在当前目录的父目录中搜索一个settings.gradle
文件。如果
settings.gradle
仍然没有找到,项目将作为单项目构建执行。如果找到
settings.gradle
文件,并且当前项目是多项目定义的一部分,则该项目将作为多项目构建的一部分执行。否则,项目将作为单项目构建执行。
我们可以强制 Gradle 不在父目录中查找 settings.gradle
文件,使用 --no-search-upward
(或-u
)命令行参数。
在我们当前的项目设置中,我们定义了项目的分层布局。我们将 settings.gradle
文件放在父目录下,并通过 include()
方法,我们添加了 <将 code class="literal">tree 和 flower
项目添加到我们的多项目构建中。
我们还可以使用平面布局来设置我们的多项目构建,可以如下完成:
我们首先要在
garden
目录下创建一个master
目录。我们必须将我们的
build.gradle
和settings.gradle
文件从garden
目录到master
目录。由于我们不再有分层布局,我们必须将
include()
方法替换为includeFlat()
方法。我们的settings.gradle
文件现在看起来类似于以下代码:// Include tree and flower projects // as part of the build. includeFlat('tree', 'flower')
这些项目是通过
master
目录的父目录引用的。因此,如果我们将tree
定义为includeFlat()
方法的参数,则用于解析项目目录是master/../tree
。要为每个项目调用
printInfo
任务,我们使用以下命令从master
目录运行 Gradle:master $ gradle printInfo Unresolved directive in Gradle-Effective-Implementation-Guide_07_ 1stDraft.adoc -include::/Users/mrhaki/Projects/ gradle-effective-implementation-guide-2/ gradle-impl-guide- 2/src/docs/asciidoc/Chapter7/Code_Files/ multi-project/garden-proj/flat-master/printinfo.output.txt[]
我们在 tree
和 flowerbuild.gradle
文件> 具有printInfo
任务实现的项目。但是,有了 Gradle 的多项目支持,我们就不必这样做了。我们可以在根build.gradle
文件中定义所有项目任务和属性。我们可以使用它在一个地方为所有项目定义通用功能。
我们可以使用 project()
方法引用项目,并使用项目的完整名称作为参数。我们必须使用闭包来定义项目的任务和属性。
对于我们的示例项目,我们将首先从 tree
和 build.gradle
文件">花 目录。接下来,我们将更改 master
目录下的 build.gradle
文件。在这里,我们将使用 project()
方法定义 printInfo
任务>tree 和 flower
项目,如下:
task printInfo << { println "This is ${project.name}" } project(':flower') { // Add an extra action to the printInfo task. task printInfo << { println "This is ${project.name}" } } project(':tree') { // Add an extra action to the printInfo task. task printInfo << { println "This is ${project.name}" } }
如果我们从 master
目录执行printInfo
任务,我们可以看到所有 调用项目的 printInfo
任务:
master $ gradle printInfo
:printInfo
This is master
:flower:printInfo
This is flower
:tree:printInfo
This is tree
BUILD SUCCESSFUL
Total time: 0.674 secs
Gradle 还具有 allprojects{}
脚本块,用于将项目任务和属性应用于作为多项目构建一部分的所有项目。我们可以重写我们的 build.gradle
文件并使用 allprojects{}
脚本块来获得任务的清晰定义无需重复:
allprojects { // Add task printInfo to all projects: // master, flower and tree task printInfo << { println "This is ${project.name}" } }
如果我们从master
目录调用printInfo
任务,我们可以看到每个项目都有新添加的任务:
master $ gradle -q printInfo
This is master
This is flower
This is tree
如果我们只想配置tree
和 flower
子项目,我们必须使用 子项目{}
脚本块。使用此脚本块,仅配置多项目构建的子项目的任务和属性。在下面的示例构建文件中,我们将只配置 子项目:
subprojects { // Add task printInfo to all sub projects: // flower and tree task printInfo << { println "This is ${project.name}" } }
如果我们调用 printInfo
任务,我们可以看到我们的 master
项目不再有 printInfo
任务:
master $ gradle -q printInfo
This is flower
This is tree
如果没有为单个项目定义 printInfo
任务,Gradle 不会抛出异常。 Gradle 将首先为作为多项目构建一部分的所有项目构建一个完整的任务图。如果任何项目包含我们要运行的任务,则执行该项目的任务。只有当所有项目都没有任务时,Gradle 才会使构建失败。
我们可以将 allprojects{}
和 subprojects{}
脚本块和 项目()
方法来定义常见行为并为特定项目应用特定行为。在以下示例构建文件中,我们在不同级别为printInfo
任务添加了额外的功能:
allprojects { task printInfo << { println "This is ${project.name}" } } subprojects { // Add an extra action to the printInfo task. printInfo << { println "Can be planted" } } project(':tree') { // Add an extra action to the printInfo task. printInfo << { println "Has leaves" } } project(':flower') { // Add an extra action to the printInfo task. printInfo << { println "Smells nice" } }
现在当我们执行 printInfo
任务时,我们将得到以下输出:
master $ gradle printInfo
:printInfo
This is master
:flower:printInfo
This is flower
Can be planted
Smells nice
:tree:printInfo
This is tree
Can be planted
Has leaves
BUILD SUCCESSFUL
Total time: 0.631 secs
我们使用 project( )
方法。但是,我们也可以将 build.gradle
文件添加到 树
和 flower
项目并在那里添加了额外的功能。
要将特定配置应用于多个项目,我们还可以使用项目过滤。在我们的 build.gradle
文件中,我们必须使用 configure()
方法。我们将根据项目名称定义一个过滤器作为方法的参数。在闭包中,我们为每个找到的项目定义配置。
在以下示例构建文件中,我们使用项目过滤器来查找名称以 f
开头的项目,然后将配置应用于项目,如下所示:
allprojects { task printInfo << { println "This is ${project.name}" } } // Find all projects that start with an f. ext { projectsWithF = allprojects.findAll { project -> project.name.startsWith('f') } } // Configure the found projects. configure(projectsWithF) { printInfo << { println 'Smells nice' } }
当我们执行 printInfo
任务时,我们得到以下输出:
master $ gradle printInfo
:flower:printInfo
This is flower
Smells nice
BUILD SUCCESSFUL
Total time: 0.231 secs
我们使用项目名称作为过滤器。我们还可以使用项目属性来定义过滤器。由于项目属性仅在定义构建后设置,可以使用 build.gradle
文件或 project()
方法,我们必须使用 afterEvaluate()
方法。一旦配置了所有项目并设置了项目属性,就会调用此方法。我们会将自定义配置作为闭包传递给 afterEvaluate()
方法。
在以下示例构建文件中,我们读取了 hasLeaves
项目属性,用于 tree
和 花
项目。如果属性是 true
,我们为这个项目自定义 printInfo
任务:
allprojects { task printInfo << { println "This is ${project.name}" } } subprojects { // After all projects have been evaluated // the properties are set and we can check // the value. afterEvaluate { project -> if (project.hasLeaves) { printInfo << { println "Has leaves" } } } } project(':tree') { ext.hasLeaves = true } project(':flower') { ext.hasLeaves = false }
当我们从master
目录执行printInfo
任务时,我们得到以下输出:
master $ gradle printInfo
:printInfo
This is master
:flower:printInfo
This is flower
:tree:printInfo
This is tree
as leaves
BUILD SUCCESSFUL
Total time: 0.667 secs
如果我们调用 printInfo
任务,我们看到 flower的
项目在 printInfo
任务tree
项目之前执行。 Gradle 默认使用项目的字母顺序来确定任务的执行顺序。我们可以通过定义不同项目中任务之间的显式依赖关系来改变这个执行顺序。
如果我们首先要在 flower之前执行
项目,我们可以定义tree
项目的printInfo
任务flower
项目的 printInfo
任务依赖于 tree
项目的 "literal">printInfo 任务。在下面的示例构建文件中,我们将更改 flower
项目中的 printInfo
任务的依赖关系。我们将使用 dependsOn()
方法来引用 的
项目,如下:printInfo
任务树
allprojects { task printInfo << { println "This is ${project.name}" } } project(':flower') { printInfo.dependsOn(':tree:printInfo') }
如果我们执行 printInfo
任务,我们将在输出中看到printInfo
任务literal">tree 项目在 flower
项目的 printInfo
任务之前执行:
master $ gradle printInfo
:printInfo
This is master
:tree:printInfo
This is tree
:flower:printInfo
This is flower
BUILD SUCCESSFUL
Total time: 0.637 secs
除了项目之间的任务依赖外,我们还可以包含其他配置依赖。例如,我们可以让一个项目设置的项目属性被另一个项目使用。 Gradle 将按字母顺序评估项目。在下面的例子中,我们将在 tree
目录下新建一个build.gradle
文件,并在根目录下设置一个属性项目:
rootProject.ext.treeMessage = 'I am a tree'
我们还将在 flower
项目中创建一个 build.gradle
文件,并根据根项目设置一个具有值的项目属性tree
项目设置的属性,如下:
ext.message = rootProject.hasProperty('treeMessage') ? rootProject.treeMessage : 'is not set' printInfo << { println "Tree say ${message}" }
当我们执行 printInfo
任务时,我们得到以下输出:
master $ gradle printInfo
:printInfo
This is master
:flower:printInfo
This is flower
Tree say I am a tree
:tree:printInfo
This is tree
BUILD SUCCESSFUL
Total time: 0.578 secs
请注意,flower
项目中的 printInfo
任务无法显示根项目属性的值,因为该值尚未设置 tree
项目。为了改变项目的求值顺序,我们可以明确定义 flower
项目依赖于 tree
项目和 ;evaluationDependsOn()
方法。我们可以修改 flower
目录下的 build.gradle
文件,添加 evaluationDependsOn (':tree')
到文件顶部:
evaluationDependsOn(':tree') ext.message = rootProject.hasProperty('treeMessage') ? rootProject.treeMessage : 'is not set' printInfo << { println "Tree say ${message}" }
当我们再次执行 printInfo
任务时,我们在输出中看到根项目属性的值在 flower
项目:
master $ gradle printInfo
:printInfo
This is master
:flower:printInfo
This is flower
Tree say I am a tree
:tree:printInfo
This is tree
BUILD SUCCESSFUL
Total time: 0.578 secs
在 Java 项目中,我们通常在项目之间存在编译或运行时依赖关系。例如,一个项目的输出是另一个项目的编译依赖项。这在 Java 项目中很常见。让我们创建一个带有 common
项目的 Java 项目,该项目包含其他项目使用的 Java 类。我们将添加一个 services
项目,该项目引用 common
项目中的类。最后,我们将添加一个 web
项目和一个 Java servlet 类,该类使用来自 services
项目的类。
我们的项目有以下目录结构:
. ├── build.gradle ├── common │ └── src │ └── main │ └── java │ └── sample │ └── gradle │ └── util │ └── Logger.java ├── services │ └── sample │ └── src │ ├── main │ │ └── java │ │ └── sample │ │ └── gradle │ │ ├── api │ │ │ └── SampleService.java │ │ └── impl │ │ └── SampleImpl.java │ └── test │ └── java │ └── sample │ └── gradle │ └── impl │ └── SampleTest.java ├── settings.gradle └── web └── src └── main ├── java │ └── gradle │ └── sample │ └── web │ └── SampleServlet.java └── webapp └── WEB-INF └── web.xml
在根目录中,我们将创建一个 settings.gradle
文件。我们将使用 include()
方法添加 common
, web
和 services/sample
项目到构建:
include('common', 'services:sample', 'web')
接下来,我们将在根目录下创建一个 build.gradle
文件。我们将为每个子项目应用 Java 插件,并为 JUnit 库添加一个 testCompile
依赖项。此配置应用于我们构建中的每个子项目。我们的 :services:sample
项目依赖于 common
项目。我们会在 :services:sample
的项目配置中配置这个依赖。我们将使用 project()
方法来定义这个项目间依赖关系。我们的 Web 项目使用 :common
和 :services:sample
项目中的类。我们只需要定义对 :services:sample
项目的依赖。 Gradle 会自动将此项目的依赖添加到:web
项目中。在我们的项目中,这意味着 :common
项目也被添加为传递项目依赖,我们可以使用 Logger
SampleServlet
类中来自该项目的类。我们将为 servlet API 添加另一个外部依赖项到我们的 :web
项目,并将 war
插件应用到我们的 < code class="literal">:web 项目,如下:
subprojects { apply plugin: 'java' repositories { mavenCentral() } dependencies { testCompile 'junit:junit:4.8.12' } } project(':services:sample') { dependencies { // Dependency on the common project classes. compile project(':common') } } project(':web') { apply plugin: 'war' dependencies { // Dependency on the sample classes. compile project(':services:sample') compile 'javax.servlet:servlet-api:2.5' } }
项目依赖项也称为 lib 依赖项。这些依赖项用于评估项目的执行顺序。 Gradle 将分析依赖关系,然后决定首先需要构建的项目,以便依赖的项目可以使用生成的类。
让我们从根目录使用以下命令构建我们的项目:
$ gradle build
:common:compileJava
:common:processResources UP-TO-DATE
:common:classes
:common:jar
:common:assemble
:common:compileTestJava UP-TO-DATE
:common:processTestResources UP-TO-DATE
:common:testClasses UP-TO-DATE
:common:test UP-TO-DATE
:common:check UP-TO-DATE
:common:build
:services:compileJava UP-TO-DATE
:services:processResources UP-TO-DATE
:services:classes UP-TO-DATE
:services:jar
:services:assemble
:services:compileTestJava UP-TO-DATE
:services:processTestResources UP-TO-DATE
:services:testClasses UP-TO-DATE
:services:test UP-TO-DATE
:services:check UP-TO-DATE
:services:build
:services:sample:compileJava
:services:sample:processResources UP-TO-DATE
:services:sample:classes
:services:sample:jar
:web:compileJava
:web:processResources UP-TO-DATE
:web:classes
:web:war
web:assemble
:web:compileTestJava UP-TO-DATE
:web:processTestResources UP-TO-DATE
:web:testClasses UP-TO-DATE
:web:test UP-TO-DATE
:web:check UP-TO-DATE
:web:build
:services:sample:assemble
:services:sample:compileTestJava
:services:sample:processTestResources UP-TO-DATE
:services:sample:testClasses
:services:sample:test
:services:sample:check
:services:sample:build
BUILD SUCCESSFUL
Total time: 3.786 secs
执行了很多任务,但我们不必担心它们的依赖关系。 Gradle 将确保执行正确的任务顺序。
我们还可以基于项目中的配置来拥有项目依赖项。假设我们在:services:sample
项目中定义了一个单独的 JAR 工件,其中只有 SampleService
类。我们可以将它作为一个单独的依赖项添加到我们的:web
项目中。在以下示例构建文件中,我们将使用 SampleService
类创建一个新的 JAR 文件,然后将其用作 中的 lib 依赖项:web
项目:
subprojects { apply plugin: 'java' repositories { mavenCentral() } dependencies { testCompile 'junit:junit:4.8.2' } } project(':services:sample') { configurations { api } task apiJar(type: Jar) { baseName = 'api' dependsOn classes from sourceSets.main.output include 'sample/gradle/api/SampleService.class' } artifacts { api apiJar } dependencies { compile project(':common') } } project(':web') { apply plugin: 'war' dependencies { compile project(path: ':services:sample', configuration: 'api') compile project(':services:sample') compile 'javax.servlet:servlet-api:2.5' } }
由于项目之间的 lib 依赖关系,我们可以在 Gradle 中执行部分构建。这意味着我们不必在项目的根目录中构建必要的项目。我们可以切换到项目目录并从那里调用构建任务,Gradle 将首先构建所有必要的项目,然后是当前项目。
让我们切换到 services/sample
目录,从那里调用构建任务,然后检查输出:
$ cd services/sample
sample $ gradle build
:common:compileJava
:common:processResources UP-TO-DATE
:common:classes
:common:jar
:services:sample:compileJava
:services:sample:processResources UP-TO-DATE
:services:sample:classes
:services:sample:jar
:services:sample:assemble
:services:sample:compileTestJava
:services:sample:processTestResources UP-TO-DATE
:services:sample:testClasses
:services:sample:test
:services:sample:check
:services:sample:build
BUILD SUCCESSFUL
Total time: 1.676 secs
:common
项目是在我们的:services:sample
项目之前构建的。如果我们不想构建我们所依赖的项目,我们必须使用 --no-rebuild
(或- a
) 命令行参数。然后,Gradle 将跳过构建我们项目所依赖的项目,并将使用缓存版本的依赖项。
当我们在调用 build
任务时使用 -a
参数时,我们会得到以下输出:
sample $ gradle -a build
:services:sample:compileJava UP-TO-DATE
:services:sample:processResources UP-TO-DATE
:services:sample:classes UP-TO-DATE
:services:sample:jar UP-TO-DATE
:services:sample:assemble UP-TO-DATE
:services:sample:compileTestJava UP-TO-DATE
:services:sample:processTestResources UP-TO-DATE
:services:sample:testClasses UP-TO-DATE
:services:sample:test UP-TO-DATE
:services:sample:check UP-TO-DATE
:services:sample:build UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.638 secs
如果我们在我们的 :services:sample
项目中调用 build
任务,则 : common
项目也已构建。但是,有一个问题,因为只有 :common
项目的 jar
任务被执行。通常, build
任务也会运行测试并执行 check
任务。仅当项目构建为 lib 依赖项时,Gradle 才会跳过这些任务。
如果我们要执行依赖项目的测试和检查,我们必须执行 buildNeeded
任务。 Gradle 然后将执行所有依赖项目的完整构建。让我们从 s ervices/sample
目录执行 buildNeeded
任务并查看输出:
sample $ gradle buildNeeded
:common:compileJava UP-TO-DATE
:common:processResources UP-TO-DATE
:common:classes UP-TO-DATE
:common:jar UP-TO-DATE
:common:assemble UP-TO-DATE
:common:compileTestJava UP-TO-DATE
:common:processTestResources UP-TO-DATE
:common:testClasses UP-TO-DATE
:common:test UP-TO-DATE
:common:check UP-TO-DATE
:common:build UP-TO-DATE
:common:buildNeeded UP-TO-DATE
:services:sample:compileJava UP-TO-DATE
:services:sample:processResources UP-TO-DATE
:services:sample:classes UP-TO-DATE
:services:sample:jar UP-TO-DATE
:services:sample:assemble UP-TO-DATE
:services:sample:compileTestJava UP-TO-DATE
:services:sample:processTestResources UP-TO-DATE
:services:sample:testClasses UP-TO-DATE
:services:sample:test UP-TO-DATE
:services:sample:check UP-TO-DATE
:services:sample:build UP-TO-DATE
:services:sample:buildNeeded UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.651 secs
如果我们对 :services:sample
项目进行了更改,我们可能还需要依赖于 sample
项目的项目被建造。我们可以使用它来确保我们没有破坏任何依赖于我们项目的代码。 Gradle 有一个 buildDependents
任务来执行此操作。例如,让我们从我们的 :services:sample
项目执行这个任务,我们的 :web
项目也是这样构建的依赖于 :services:sample
项目。当我们执行 buildDependents
任务时,我们会得到如下输出:
sample $ gradle buildDependents
:common:compileJava UP-TO-DATE
:common:processResources UP-TO-DATE
:common:classes UP-TO-DATE
:common:jar UP-TO-DATE
:services:sample:compileJava UP-TO-DATE
:services:sample:processResources UP-TO-DATE
:services:sample:classes UP-TO-DATE
:services:sample:apiJar
:services:sample:jar UP-TO-DATE
:web:compileJava
:web:processResources UP-TO-DATE
:web:classes
:web:war
:web:assemble
:web:compileTestJava UP-TO-DATE
:web:processTestResources UP-TO-DATE
:web:testClasses UP-TO-DATE
:web:test UP-TO-DATE
:web:check UP-TO-DATE
:web:build
:web:buildDependents
:services:sample:assemble UP-TO-DATE
:services:sample:compileTestJava UP-TO-DATE
:services:sample:processTestResources UP-TO-DATE
:services:sample:testClasses UP-TO-DATE
:services:sample:test UP-TO-DATE
:services:sample:check UP-TO-DATE
:services:sample:build UP-TO-DATE
:services:sample:buildDependents
BUILD SUCCESSFUL
Total time: 0.709 secs
在上一节中,我们创建了一个带有 web
子项目的 Java 项目。 web
项目有一个简单的 servlet。要执行 servlet,我们必须创建 WAR 文件并将 WAR 文件部署到 servlet 容器,例如 Tomcat 或 Jetty。您可以在 http://www.eclipse.org/jetty/< /一>。使用 Jetty 插件,我们可以在 Jetty Web 容器中从命令行运行我们的web
项目。我们不必在我们的电脑上安装 Jetty,我们只需要将 Jetty 插件应用到我们的项目中。该插件将负责配置 Jetty 并启动 Web 容器。如果一切正常,我们可以打开 Web 浏览器并访问我们的 servlet。
要将 Jetty 插件添加到我们的 web
项目中,让我们在 web 目录中创建一个新的 build.gradle
文件。在这里,我们将使用 apply()
方法将Jetty插件添加到项目中:
apply plugin: 'jetty'
该插件为我们的项目添加了以下任务:jettyRun
、 jettyRunWar
和 jettyStop
。下表显示了不同的任务:
任务 |
取决于 |
类型 |
说明 |
|
|
|
这是启动一个 Jetty web 容器并部署爆炸的 web 应用程序 |
|
|
|
这是启动一个 Jetty web 容器并部署 WAR 文件 |
|
- |
|
这是为了停止正在运行的 Jetty Web 容器 |
执行 jettyRun
或jettyWar
任务后,我们可以在 Web 浏览器中测试我们的 servlet。当我们从多项目构建的根目录执行 jettyRun
任务时,我们得到以下输出:
$ gradle :web:jettyRun
:common:compileJava
:common:processResources UP-TO-DATE
:common:classes
:common:jar
:services:sample:compileJava
:services:sample:processResources UP-TO-DATE
:services:sample:classes
:services:sample:apiJar
:services:sample:jar
:web:compileJava
:web:processResources UP-TO-DATE
:web:classes
> Building 92% > :web:jettyRun > Running at http://localhost:8080/web
Gradle 将继续运行,最后,我们将看到应用程序正在运行在 http://localhost:8080/web
。我们可以打开网络浏览器并访问我们的网络应用程序。在以下屏幕截图中,我们可以看到 servlet 的输出:
要停止 Jetty Web 容器,请按 Ctrl + C命令行返回到我们的提示符。
我们可以通过Jetty插件添加的httpPort
项目约定属性或httpPort
任务属性更改端口号 jettyRun
和 jettyRunWar
任务。要更改上下文路径,我们可以设置 jettyRun
和 contextPath
属性>jettyRunWar 任务。
如果我们希望 Jetty 容器自动扫描更改,我们可以将 reload
属性设置为 automatic。
如果设置了该属性要 manual
,我们必须在命令行上按 Enter重新加载更改。我们可以使用 scanIntervalSeconds
属性以秒为单位设置扫描间隔。
在以下示例构建文件中,我们将使用另一个 HTTP 端口、上下文路径和自动重新加载来自定义 Jetty Web 容器:
apply plugin: 'jetty' httpPort = 8090 jettyRun { contextPath = 'sample' reload = 'automatic' scanIntervalSeconds = 10 }
我们甚至可以使用自定义 Jetty 配置文件进一步自定义 Jetty 容器。我们可以使用 jettyRun
任务属性 jettyConfig
来使用配置文件。我们还可以使用 additionalRuntimeJars
属性添加额外的运行时库。
如果我们要使用 jettyStop
任务,我们还必须定义 stopPort
和 我们的项目或任务中的 stopKey
属性。如果我们定义了这些属性,我们可以打开一个新的命令行提示符并调用 jettyStop
任务来停止正在运行的 Jetty Web 容器。
在以下示例构建文件中,我们将应用其中一些属性和方法来自定义 Jetty 配置:
apply plugin: 'jetty' configurations { // Extra configuration to // be used in the jettyRun task. jettyAdditionalLibs } dependencies { jettyAdditionalLibs 'org.slf4j:slf4j-simple:1.7.3' } // Properties for stopping Jetty with jettyStop stopPort = 8109 stopKey = 'JettyStop' jettyRun { // External Jetty configuration file. jettyConfig = file('src/jetty/jetty.xml') // Extra libraries for Jetty runtime. additionalRuntimeJars configurations.jettyAdditionalLibs }
多项目构建在软件项目中非常常见。 Gradle 对多项目构建有很好的支持。我们可以使用分层布局作为项目结构,但我们可以轻松自定义此布局并使用其他布局。
配置项目很容易,并且可以在一个地方完成——”在项目的根目录。我们还可以在项目级别本身添加项目配置。我们不仅可以在项目库级别定义项目之间的依赖关系,还可以通过配置或任务依赖关系来实现。 Gradle 将解决构建完整项目的正确方法,这样我们就不必太担心了。
由于 Gradle 在执行任务之前知道将涉及的项目,因此我们可以进行部分多项目构建。 Gradle 会自动构建项目依赖,这是我们当前项目所必需的,我们可以使用单个任务来构建依赖于我们当前项目的项目。
我们还看到了如何使用 Jetty 插件在 Jetty Web 容器中运行我们的 Web 应用程序代码。我们应用了插件并执行了 jettyRun
或 jettyRunWar
任务以将我们的代码作为 Web 应用程序运行。我们现在可以打开网络浏览器并执行我们的代码。
在下一章中,我们将看看如何在 Gradle 中使用除 Java 之外的其他语言。