Grails 多项目构建
学习如何在 Grails 应用程序中利用 Gradle 功能来创建多项目构建
作者:Sergio del Amo
Grails 版本 4.0.1
1 Grails 培训
Grails 培训 - 由创建并积极维护 Grails 框架人员开发并交付!.
2 入门
在本指南中,您将从头开始创建一个多项目构建。我们将组合一个 Grails 应用、一个 Grails 插件和一个简单的 Groovy Lib。
2.1 所需材料
要完成本指南,您需要:
-
一些时间
-
合适的文本编辑器或 IDE
-
安装了 JDK 1.8 或更高版本,并适当地配置了
JAVA_HOME
2.2 如何完成指南
要开始操作,请执行以下操作:
-
下载并解压缩源码
或
-
克隆 Git 代码库
git clone https://github.com/grails-guides/grails-multi-project-build.git
Grails 指南代码库包含两个文件夹
-
initial
初始项目。通常是包含一些附加代码以让您领先一步的简单 Grails 应用。 -
complete
完成后的示例。这是完成指南中介绍的步骤并将这些更改应用于initial
文件夹的结果。
要完成指南,请转到 initial
文件夹
-
cd
到grails-guides/grails-multi-project-build/initial
并按照下一部分中的说明进行操作。
如果您 cd 到 grails-guides/grails-multi-project-build/complete ,则可以转到完成的示例 |
3 编写应用程序
Grails 使用 Gradle Build System 来执行编译、运行测试和生成项目二进制发行版等构建相关任务。
Gradle 对多项目构建的强大支持是 Gradle 的独特卖点之一。
Gradle 中的多项目构建由一个根项目和一个或多个子项目组成,这些子项目也可能有子项目。
3.1 概述
我们要创建一个应用程序,该应用程序将 Grails App 构建与 web
配置文件、具有 plugin
配置文件的 Grails 插件和一个简单的 Groovy 库相结合。
3.2 创建项目
创建根项目
$ mkdir multiproject
$ cd multiproject
使用 web
配置文件创建 Grails App
multiproject$ mkdir app
multiproject$ cd app
multiproject/app$ grails
grails> create-app --profile web --inplace
| Application created at multiproject/app
| Resolving Dependencies. Please wait…
CONFIGURE SUCCESSFUL
Total time: 12.262 secs
grails> exit
multiproject/app$ cd ..
使用 plugin
配置文件创建 Grails 插件
multiproject$ mkdir plugin
multiproject$ cd plugin
multiproject/plugin$ grails
grails> create-app --profile plugin --inplace
| Application created at multiproject/plugin
| Resolving Dependencies. Please wait…
CONFIGURE SUCCESSFUL
Total time: 12.262 secs
grails> exit
multiproject/plugin$ cd ..
创建 Groovy 库
multiproject$ mkdir groovylib
multiproject$ cd groovylib
multiproject/groovylib$ touch build.gradle
为 Groovy 库创建一个构建文件
plugins {
id 'groovy'
}
repositories {
jcenter()
}
dependencies {
implementation 'org.codehaus.groovy:groovy-all:2.5.8'
}
将 Gradle 文件移动到根项目
multiproject$ mv app/gradlew .
multiproject$ mv app/gradlew.bat .
multiproject$ mv app/gradle.properties .
multiproject$ mv app/gradle .
将项目添加到 settings.gradle
include 'app'
include 'plugin'
include 'groovylib'
删除不必要的文件
清理插件文件夹
multiproject$ rm plugin/gradlew
multiproject$ rm plugin/gradlew.bat
multiproject$ rm plugin/grailsw.bat
multiproject$ rm plugin/grailsw
multiproject$ rm plugin/grailsw.bat
multiproject$ rm plugin/grails-wrapper.jar
multiproject$ rm plugin/settings.gradle
multiproject$ rm plugin/gradle.properties
multiproject$ rm -rf plugin/gradle
multiproject$ rm plugin/.gitignore
清理应用程序文件夹
multiproject$ rm app/grailsw
multiproject$ rm app/grailsw.bat
multiproject$ rm app/grails-wrapper.jar
multiproject$ rm app/settings.gradle
multiproject$ rm app/.gitignore
3.3 项目的依赖项
我们想要配置以下依赖项
应用程序依赖项
app
依赖于 plugin
。你可以在 dependencies
块中表示它,如下所示
dependencies {
...
compile project(':plugin')
}
或使用
grails {
plugins {
compile project(':plugin')
}
}
插件依赖项
plugin
依赖于 groovylib
dependencies {
...
compile project(':groovylib')
}
3.4 在 IDE 中打开项目
可以通过告诉 IDE 打开 multiproject
文件夹,使用 IDE(例如 IntelliJ IDEA)打开 Grails 多项目构建。
导入项目后,你会看到窗口
3.5 多个项目中的代码
我们将代码添加到不同的项目,以验证多项目构建是否正常工作。
package app
import demo.RandomPersonService
import groovy.transform.CompileStatic
@CompileStatic
class PersonController {
RandomPersonService randomPersonService
def index() {
render randomPersonService.randomOciPersonName()
}
}
package demo
import groovy.transform.CompileStatic
@CompileStatic
class RandomPersonService {
String randomOciPersonName() {
List<String> people = OCI.PEOPLE
Collections.shuffle(people)
people.first()
}
}
package demo
import groovy.transform.CompileStatic
@CompileStatic
class OCI {
public static final List<String> PEOPLE = [
'Ryan',
'Jeff',
'Paul',
'Søren',
'Sergio'
]
}
为了验证我们的应用程序,我们使用 Rest Client Builder Grails 插件。将插件添加到我们的 app
依赖项
testCompile "io.micronaut:micronaut-http-client"
package app
import demo.OCI
import grails.testing.mixin.integration.Integration
import grails.testing.spock.OnceBefore
import io.micronaut.http.HttpRequest
import io.micronaut.http.client.HttpClient
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification
@Integration
class PersonControllerSpec extends Specification {
@Shared
@AutoCleanup
HttpClient client
@OnceBefore
void init() {
String baseUrl = "http://localhost:$serverPort"
this.client = HttpClient.create(new URL(baseUrl))
}
def '/person endpoints return one of the names'() {
when:
String text = client.toBlocking().retrieve(HttpRequest.GET('/person'), String)
then:
OCI.PEOPLE.contains text
}
}
3.6 保持构建简洁
如果比较 app/build.gradle
和 plugin/build.gradle
,你会看到很多重复。为了消除重复,我们将添加 ROOT build.gradle
buildscript {
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
classpath "com.bertramlabs.plugins:asset-pipeline-gradle:$assetPipelineVersion"
classpath "org.grails.plugins:hibernate5:7.0.0"
classpath "gradle.plugin.com.github.erdi.webdriver-binaries:webdriver-binaries-gradle-plugin:2.1"
}
}
ext {
grailsApps = ['app']
grailsPlugins = ['plugin']
}
subprojects { project ->
boolean isGrailsApp = grailsApps.contains(project.name)
boolean isGrailsPlugin = grailsPlugins.contains(project.name)
boolean isGrailsProject = isGrailsApp || isGrailsPlugin
if ( isGrailsProject ) {
apply plugin:"eclipse"
apply plugin:"idea"
if ( isGrailsApp ) {
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"org.grails.grails-gsp"
apply plugin:"com.bertramlabs.asset-pipeline"
apply plugin:"com.github.erdi.webdriver-binaries"
}
if ( isGrailsPlugin ) {
apply plugin:"org.grails.grails-plugin"
apply plugin:"org.grails.grails-plugin-publish"
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
content {
includeVersionByRegex('io\\.micronaut.*', '.*', '.*BUILD-SNAPSHOT')
}
}
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-logging"
compile "org.springframework.boot:spring-boot-autoconfigure"
compile "org.grails:grails-core"
console "org.grails:grails-console"
}
if ( isGrailsApp ) {
configurations {
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
}
dependencies {
developmentOnly("org.springframework.boot:spring-boot-devtools")
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-tomcat"
compile "org.grails:grails-dependencies"
compile "org.grails:grails-web-boot"
compile "org.grails.plugins:cache"
compile "org.grails.plugins:scaffolding"
compile "org.grails.plugins:hibernate5"
compile "org.hibernate:hibernate-core:5.4.0.Final"
compile "io.micronaut:micronaut-inject-groovy"
profile "org.grails.profiles:web"
runtime "org.glassfish.web:el-impl:2.1.2-b03"
runtime "com.h2database:h2"
runtime "org.apache.tomcat:tomcat-jdbc"
runtime "com.bertramlabs.plugins:asset-pipeline-grails:$assetPipelineVersion"
testCompile "org.grails:grails-gorm-testing-support"
testCompile "org.grails:grails-web-testing-support"
}
apply from: "${rootProject.projectDir}/gradle/geb.gradle"
}
if ( isGrailsPlugin ) {
dependencies {
profile "org.grails.profiles:plugin"
provided "org.grails:grails-plugin-services"
provided "org.grails:grails-plugin-domain-class"
testCompile "org.grails:grails-gorm-testing-support"
}
}
bootRun {
jvmArgs(
'-Dspring.output.ansi.enabled=always',
'-noverify',
'-XX:TieredStopAtLevel=1',
'-Xmx1024m')
sourceResources sourceSets.main
}
if (isGrailsApp) {
webdriverBinaries {
chromedriver "$chromeDriverVersion"
geckodriver "$geckodriverVersion"
}
tasks.withType(Test) {
systemProperty "geb.env", System.getProperty('geb.env')
systemProperty "geb.build.reportsDir", reporting.file("geb/integrationTest")
systemProperty "webdriver.chrome.driver", System.getProperty('webdriver.chrome.driver')
systemProperty "webdriver.gecko.driver", System.getProperty('webdriver.gecko.driver')
}
}
if ( isGrailsPlugin ) {
// enable if you wish to package this plugin as a standalone application
bootJar.enabled = false
grailsPublish {
// TODO: Provide values here
user = 'user'
key = 'key'
githubSlug = 'foo/bar'
license {
name = 'Apache-2.0'
}
title = "My Plugin"
desc = "Full plugin description"
developers = [johndoe:"John Doe"]
portalUser = ""
portalPassword = ""
}
}
}
}
`app` 和 `plugin` gradle 文件非常简单
version "0.1"
group "app"
dependencies {
testCompile "io.micronaut:micronaut-http-client"
}
grails {
plugins {
compile project(':plugin')
}
}
version "0.1"
group "plugin"
dependencies {
compile project(':groovylib')
}
3.7 功能作为 Gradle 文件
保持构建简洁的另一种方法是为不同功能创建特定的 Gradle 文件。
假设你在多项目构建的多个项目中有 Geb 测试。
我们可以封装一组依赖项,以运行 针对多个浏览器的 Geb 测试。
dependencies {
testCompile "org.grails.plugins:geb"
testRuntime "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"
testRuntime "org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion"
testCompile "org.seleniumhq.selenium:htmlunit-driver:2.35.1"
testRuntime 'net.sourceforge.htmlunit:htmlunit:2.35.0'
testCompile "org.seleniumhq.selenium:selenium-remote-driver:$seleniumVersion"
testCompile "org.seleniumhq.selenium:selenium-api:$seleniumVersion"
testCompile "org.seleniumhq.selenium:selenium-support:$seleniumVersion"
}
然后,你可以将这些依赖项应用于特定项目
apply from: "${rootProject.projectDir}/gradle/geb.gradle"
4 运行应用程序
要运行应用程序
./gradlew app:bootRun
要运行测试
./gradlew check