Apache Grails 快速入门与集成示例

从零快速上手 Apache Grails 6.x,涵盖环境准备、首个应用创建运行与结构解读,并给出 GORM 域类加 REST 控制器的示例应用、访问验证,以及与 Spring Boot、MySQL 集成的样例与输出。

Apache GrailsApache GrailsJavaApache2026/10/12

Apache Grails 快速入门与集成示例

本文面向初次接触 Apache Grails 的开发者,从环境准备与首个应用入手,逐步给出 GORM 域类、REST 控制器以及与 Spring Boot、数据库集成的完整示例。示例登录账号统一为 root / q1w2e3r4。

一、环境准备

1.1 依赖要求

组件版本要求
JDKJDK 11 或 JDK 17
Grails6.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 依据 web profile 生成 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,由迁移工具管理表结构。