Apache Grails 快速入门与集成示例
从零快速上手 Apache Grails 6.x,涵盖环境准备、首个应用创建运行与结构解读,并给出 GORM 域类加 REST 控制器的示例应用、访问验证,以及与 Spring Boot、MySQL 集成的样例与输出。
Apache Grails 快速入门与集成示例
本文面向初次接触 Apache Grails 的开发者,从环境准备与首个应用入手,逐步给出 GORM 域类、REST 控制器以及与 Spring Boot、数据库集成的完整示例。示例登录账号统一为 root / q1w2e3r4。
一、环境准备
1.1 依赖要求
| 组件 | 版本要求 |
|---|---|
| JDK | JDK 11 或 JDK 17 |
| Grails | 6.x(本文示例为 6.2.3) |
| 构建工具 | Gradle(随工程自带 Wrapper) |
| 数据库 | 开发用内存 H2,生产可用 MySQL 8 |
1.2 确认环境
java -version
# openjdk version "17.0.x"
grails --version
# | Grails Version: 6.2.3
# | JVM Version: 17.0.x
二、第一个应用
2.1 创建工程
mkdir -p /root/grails-lab && cd /root/grails-lab
grails create-app demoapp
实测回显:
| Application created at /root/grails-lab/demoapp
2.2 启动应用
cd demoapp
export JAVA_OPTS="-Xmx512m" # JVM 堆限制在 256–512MB 规范内
grails run-app
实测回显:
Grails application running at http://localhost:8080 in environment: development
2.3 访问首页
curl -i http://localhost:8080/
实测回显(节选):
HTTP/1.1 200
Content-Type: text/html;charset=UTF-8
<title>Welcome to Grails</title>
2.4 解读
create-app依据webprofile 生成 Spring Boot 应用骨架。run-app启动内嵌 Tomcat,默认监听 8080,开发环境使用 H2 内存库(dbCreate: create-drop)。- 首页由默认
UrlMappings与内置欢迎页提供。
三、项目结构解读
demoapp/
├── build.gradle # 依赖与插件声明
├── gradlew / grailsw # 构建包装脚本
├── grails-app/
│ ├── conf/
│ │ ├── application.yml # 主配置
│ │ └── logback.xml # 日志配置
│ ├── controllers/demoapp/ # 控制器(URL 端点)
│ ├── domain/demoapp/ # GORM 域类(映射数据库表)
│ ├── services/demoapp/ # 业务服务
│ ├── views/ # GSP 视图
│ └── init/ # BootStrap 初始化
└── src/
├── main/groovy/ # 其他 Groovy/Java 源码
├── test/groovy/ # 单元测试
└── integration-test/ # 集成测试
grails-app/conf/application.yml 默认关键片段:
grails:
profile: web
controllers:
defaultScope: singleton
dataSource:
pooled: true
driverClassName: org.h2.Driver
username: sa
password: ''
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
四、集成示例一:GORM 域类 + REST 控制器
以下示例构建一个 Book 资源的增删改查 REST 接口。
4.1 创建域类
grails create-domain-class demoapp.Book
编辑 grails-app/domain/demoapp/Book.groovy:
package demoapp
class Book {
String title
String author
BigDecimal price
static constraints = {
title blank: false, maxSize: 255
author nullable: true
price nullable: true, min: 0.0
}
static mapping = {
table 'book'
}
}
4.2 创建 REST 控制器
grails create-controller demoapp.BookController
编辑 grails-app/controllers/demoapp/BookController.groovy:
package demoapp
import grails.converters.JSON
class BookController {
static responseFormats = ['json']
// GET /book 列出全部
def index() {
respond Book.list(), model: [bookList: Book.list()]
}
// GET /book/$id 查询单个
def show(Long id) {
def book = Book.get(id)
if (!book) { render status: 404, text: 'Not Found'; return }
render book as JSON
}
// POST /book 创建
def save() {
def book = new Book(request.JSON)
if (book.save(flush: true)) {
render book as JSON
} else {
render status: 422, text: book.errors.toString()
}
}
// PUT /book/$id 更新
def update(Long id) {
def book = Book.get(id)
if (!book) { render status: 404, text: 'Not Found'; return }
book.properties = request.JSON
book.save(flush: true)
render book as JSON
}
// DELETE /book/$id 删除
def delete(Long id) {
Book.get(id)?.delete(flush: true)
render status: 204
}
}
在 grails-app/controllers/demoapp/UrlMappings.groovy 中保留默认 RESTful 映射(Grails 默认已提供 /$controller/$action?/$id?):
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{ constraints { } }
"/"(view: "/index")
"500"(view: '/error')
}
}
4.3 简单响应端点(最小验证)
若只想快速验证路由,可用一个渲染 JSON 的最小控制器:
package demoapp
import grails.converters.JSON
class HelloController {
def index() {
render([status: "ok", framework: "Apache Grails", version: "6.2.3"] as JSON)
}
}
实测回显(curl -i http://localhost:8080/hello):
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
{"status":"ok","framework":"Apache Grails","version":"6.2.3"}
五、访问验证
5.1 启动
export JAVA_OPTS="-Xmx512m"
grails run-app
5.2 采集接口响应
创建资源:
curl -i -X POST http://localhost:8080/book \
-H 'Content-Type: application/json' \
-d '{"title":"Apache Grails 实战","author":"quzuo","price":59.9}'
示例输出:
HTTP/1.1 201
Content-Type: application/json;charset=UTF-8
{"id":1,"title":"Apache Grails 实战","author":"quzuo","price":59.9}
查询列表与单个资源:
curl -s http://localhost:8080/book
curl -s http://localhost:8080/book/1
5.3 路由查看
grails url-mappings-report
会打印应用所有 URL 与控制器/动作的对应关系,便于核对接口路径。
六、集成示例二:Spring Boot 集成
Grails 构建于 Spring Boot 之上,可直接使用 Spring Boot 的能力。
6.1 依赖版本管理
build.gradle 中 Grails 插件已引入 Spring Boot 依赖管理:
plugins {
id 'org.apache.grails.gradle.grails-web' version '6.2.3'
id 'org.apache.grails.gradle.grails-gsp' version '6.2.3'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
6.2 注入 Spring Bean
在 Grails 服务中直接使用 Spring 注解:
package demoapp
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
@Service
class GreetingService {
@Value('${app.greeting:Hello}')
String greeting
String greet(String name) {
"${greeting}, ${name}!"
}
}
application.yml 中定义:
app:
greeting: 你好
控制器中通过依赖注入使用:
class GreetingController {
GreetingService greetingService
def index() {
render greetingService.greet(params.name ?: 'Grails')
}
}
6.3 健康检查
开发环境默认开启 Actuator:
environments:
development:
management:
endpoints:
enabled-by-default: true
web:
base-path: '/actuator'
exposure:
include: '*'
访问:
curl -s http://localhost:8080/actuator/health
# {"status":"UP"}
6.4 打包为可执行 Spring Boot 应用
grails assemble
java -Xmx512m -jar build/libs/demoapp-0.1.jar
七、集成示例三:MySQL 数据库集成
7.1 添加驱动依赖
build.gradle:
dependencies {
runtimeOnly 'mysql:mysql-connector-java:8.0.33'
}
7.2 配置数据源
grails-app/conf/application.yml:
dataSource:
pooled: true
driverClassName: com.mysql.cj.jdbc.Driver
dialect: org.hibernate.dialect.MySQL8Dialect
username: 'grails'
password: 'q1w2e3r4'
environments:
production:
dataSource:
dbCreate: none
url: jdbc:mysql://127.0.0.1:3306/grailsdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
7.3 准备数据库
mysql -uroot -pq1w2e3r4 -e "CREATE DATABASE grailsdb DEFAULT CHARACTER SET utf8mb4;"
7.4 以生产环境运行
grails prod run-app
启动日志中应出现 Hibernate 方言与连接池初始化信息;随后对 /book 的写入将持久化到 MySQL。
八、常见问题
Q1:grails run-app 一直显示下载?
首次运行需拉取 Gradle 发行包与依赖,耐心等待或预热 ~/.gradle 缓存。
Q2:控制器改动后需要重启吗? 开发模式支持热重载,保存后再次请求即可生效;配置类改动建议重启。
Q3:respond 与 render 有何区别?
render 直接写响应体;respond 依据内容协商(Accept 头/扩展名)自动选择格式,适合 REST。
Q4:如何在生产替换 H2?
修改 environments.production.dataSource 并引入对应 JDBC 驱动,dbCreate 设为 none,由迁移工具管理表结构。