17、Grails开发全解析:环境、配置与功能实现

Grails开发全解析:环境、配置与功能实现

1. 应用规模快速查看

在Grails应用开发中,若想快速了解新应用的规模,可使用 grails stats 命令。以下是执行该命令后的示例输出:

$ grails stats
+----------------------+-------+-------+
| Name                 | Files | LOC   |
+----------------------+-------+-------+
| Controllers          | 1     | 66    |
| Domain Classes       | 1     | 8     |
| Integration Tests    | 1     | 4     |
+----------------------+-------+-------+
| Totals               | 3     | 78    |
+----------------------+-------+-------+

通过这个输出,我们能清晰看到控制器、领域类和集成测试文件的数量以及代码行数,对应用规模有一个直观的认识。

2. 理解Grails环境

Grails默认提供了三种标准环境:开发(development)、测试(test)和生产(production)。这些环境便于根据运行模式更改数据库连接、Log4j设置等。不同环境有不同的行为特点:
- 开发模式(dev) :所有文件会自动重新加载,这意味着开发者在修改代码后无需重启服务器,但动态重新加载会在一定程度上影响性能。
- 生产模式(prod) :更注重速度优化,而非灵活性。

2.1 环境配置文件

要查看每个环境的配置设置,可查看 grails-app/conf/DataSource.groovy 文件。该文件中,环境块外的设置是全局的,而像 development 这样的环境块内的设置可以选择性地覆盖全局设置。

虽然 grails-app/conf/Config.groovy 文件中默认没有环境块,但我们可以自行添加。以下是一个示例:

environments{
    production{
        println "I'm in production"
    }
    foo{
        println "I'm in foo"
    }
}

保存文件后,执行 grails prod run-app 命令,在控制台输出的早期会显示 I'm in production

2.2 自定义环境

默认的三种环境在命令行中可直接使用。若要设置自定义环境,则需使用 -D 标志自行设置 grails.env 属性。例如,若在 Config.groovy 中添加了上述代码,执行 grails -Dgrails.env=foo run-app 命令,控制台输出会显示 I'm in foo

3. 在不同端口运行Grails

Grails默认在8080端口运行。若要在其他端口运行,有以下两种方法:

3.1 临时指定端口

在命令行中使用 -D 标志显式指定端口,例如:

$ grails -Dserver.port=9090 run-app

这种方法适合临时测试,可在同一物理机上同时运行多个不同端口的服务器。

3.2 设置默认端口

若想始终在特定端口(如9090)运行Grails,可在 $GRAILS_HOME/scripts/Init.groovy 文件中设置默认端口:

serverPort = System.getProperty('server.port') ?
    System.getProperty('server.port').toInteger() : 9090

需要注意的是, Init.groovy 是一个GANT文件,GANT是Groovy实现的Ant,对于喜欢Ant约定但更倾向于动态语言表达的开发者来说是个不错的选择。

4. 生成WAR文件

虽然在嵌入式Jetty容器中运行Grails便于开发,但在生产环境中,很少有公司使用Jetty。不过,Grails可以生成行业标准的WAR文件,可部署到任何生产环境的应用服务器上。只需执行以下命令:

$ grails war

执行该命令后,会生成一个名为 bookstore-0.1.war 的文件,版本号和应用名称来自 application.properties 文件,可根据需要进行修改:

app.version=0.1
app.servlet.version=2.4
app.grails.version=1.0
app.name=bookstore

需要注意的是,WAR文件默认在生产模式下运行。

5. 更改数据库

嵌入式数据库HSQLDB便于快速启动应用,但大多数生产环境的Grails应用最终会依赖外部数据库。若数据库受Hibernate支持,Grails也同样支持,因为Grails对象/关系映射器(GORM)是Hibernate的Groovy封装。以下以将书店应用迁移到MySQL为例,介绍更改数据库的步骤:

5.1 设置数据库和用户

假设MySQL已安装并运行,首先以具有管理权限的用户登录:

$ mysql --user=root
mysql> create database bookstore_dev;
mysql> use bookstore_dev;
mysql> grant all on bookstore_dev.* to grails@localhost identified by 'server';
mysql> flush privileges;
mysql> quit
$ mysql --user=grails -p --database=bookstore_dev

上述命令创建了目标数据库 bookstore_dev ,并创建了一个用户 grails ,密码为 server ,该用户只能从本地登录。创建完成后,可手动测试登录以确保用户账户创建成功。

5.2 复制数据库驱动

将JDBC驱动JAR文件复制到 lib 目录:

$ cp ~/mysql-connector-java-3.1.13-bin.jar bookstore/lib

5.3 调整 DataSource.groovy 配置

grails-app/conf/DataSource.groovy 文件中进行以下配置:

dataSource {
    pooled = false
    driverClassName = "com.mysql.jdbc.Driver"
    username = "grails"
    password = "server"
}
hibernate {
    cache.use_second_level_cache=true
    cache.use_query_cache=true
    cache.provider_class='org.hibernate.cache.EhCacheProvider'
}
// 环境特定设置
environments {
    development {
        dataSource {
            // 可选值:'create', 'create-drop', 'update'
            dbCreate = "update"
            url = "jdbc:mysql://localhost:3306/bookstore_dev?autoreconnect=true"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
        }
    }
}

要让Grails指向新创建的MySQL数据库,需调整以下四个值:
| 参数 | 值 |
| -------------- | -------------------------- |
| driverClassName | com.mysql.jdbc.Driver |
| username | grails |
| password | server |
| url | jdbc:mysql://localhost:3306/bookstore_dev |

由于之前的配置中存在HSQLDB的遗留设置,在测试和生产环境中无法与MySQL配合使用,因此有两种解决方案:
- 将MySQL特定的 driverClassName username password 值移到 development 块中,并为测试和生产环境设置类似的值。
- 在其他环境块中将 url 更改为有效的MySQL连接URL。

5.4 调整 dbCreate

dbCreate 变量对应Hibernate中的 hibernate.hbm2ddl.auto 设置,它允许我们微调表生成行为。以下是不同 dbCreate 值的含义:
| 设置 | 启动时操作 | 关闭时操作 |
| ------------ | ---------------------- | -------------------- |
| create-drop | 创建表 | 删除表 |
| create | 创建或修改表 | 仅删除数据 |
| update | 创建或修改表 | 保留数据 |

默认情况下,开发环境的 dbCreate 设置为 create ,测试和生产环境设置为 update 。为避免数据丢失,建议将开发环境的 dbCreate 也设置为 update 。需要注意的是, create update 操作在修改表时较为保守,只会添加字段、延长字段长度,而不会删除字段或缩短字段长度,任何可能导致数据丢失的操作都需要开发者自行处理。若不想让Hibernate自动管理表结构,可在 DataSource.groovy 文件中注释掉 dbCreate 变量。

6. 更改主页

Grails应用的默认主页文件名为 index.gsp ,由于Jetty是符合标准的Servlet容器,也支持 index.jsp 。若同一目录下同时存在 index.jsp index.gsp ,则 index.jsp 页面优先。

6.1 重定向到现有控制器

常见的做法是将主页重定向到现有的控制器。以下是一个示例,将主页重定向到 book 控制器的 list 视图:

// web-app/index.gsp
<% response.sendRedirect("book/list") %>

6.2 使用URL映射

更灵活的方法是在 grails-app/conf/UrlMappings.groovy 文件中为根URL( / )添加自定义映射:

// grails-app/conf/UrlMappings.groovy
class UrlMappings {
    static mappings = {
        "/" (controller:"book", action:"list")
    }
}

UrlMappings.groovy 文件可让我们精细控制Grails应用中URL到控制器的映射,更多信息可查看在线文档。

7. 理解控制器和视图

Grails应用的控制器是连接视图和模型的桥梁。每个URL都对应控制器中的一个动作,在Grails URL中,控制器名称会去掉 Controller 后缀。通常情况下,控制器动作在 grails-app/views 目录中有同名的GSP文件与之对应。

理解Grails控制器主要涉及三个关键操作:重定向(redirect)、返回(return)和渲染(render)。

7.1 重定向(Redirect)

def index = { redirect(action:list,params:params) }

每个控制器都应该有一个 index 动作,它是控制器的默认动作。上述示例中,若没有其他指令, bookstore/book 请求将重定向到 list 动作,并将查询字符串参数传递给该动作。重,也可以接受一个控制器参数,例如在保存书籍时,若需要用户登录并具有足够的权限,可将请求重定向到 Logon 控制器。

7.2 返回(Return)

def list = {
    if(!params.max) params.max = 10
    [ bookList: Book.list( params ) ]
}
def show = {
    [ book : Book.get( params.id ) ]
}

在Groovy方法中,最后一行是隐式返回语句。Grails动作的最后一行返回一个值的Map给同名的GSP页面。例如, list 动作若没有提供 max 参数,将从数据库中返回一个包含十本书的ArrayList; show 动作根据 id 参数从数据库中获取书籍并传递给 show.gsp 视图。

7.3 渲染(Render)

def save = {
    def book = new Book(params)
    if(!book.hasErrors() && book.save()) {
        flash.message = "Book ${book.id} created"
        redirect(action:show,id:book.id)
    }
    else {
        render(view:'create',model:[book:book])
    }
}

render 方法是Grails控制器中最灵活的操作之一。在 save 动作中,若书籍保存成功且没有错误,将重定向到 show 动作;若保存失败,则渲染 create.gsp 视图。 render 方法不仅可以渲染GSP页面,还可以用于返回XML、Atom和RSS提要,甚至Excel电子表格。

8. 动态脚手架

在学习Grails时,我们可以使用 grails generate-all Book 命令为 Book 模型创建控制器和视图。但Grails真正的强大之处在于动态脚手架功能。

8.1 动态创建控制器和视图

以下是一个示例,展示如何使用动态脚手架为 Publisher 模型创建控制器和视图:

// grails-app/Controller/PublisherController.groovy
class PublisherController {
    def scaffold = Publisher
}

// grails-app/domain/Publisher.groovy
class Publisher{
    String name
    String address
    String city
    String state
    String zipcode
    String toString(){
        return name
    }
}

通过在控制器中添加 def scaffold = Publisher ,Grails会在运行时在内存中创建控制器和视图。这在领域类还在不断变化的早期开发阶段非常有用,开发者无需频繁重建视图即可添加或删除属性。此外,我们还可以选择性地覆盖控制器动作和视图。例如,在 PublisherController 中添加自定义的 save 动作,其他动作仍会正常工作;若想为 list.gsp 视图设置特殊的外观和感觉,可在视图目录中添加该文件。

8.2 更改字段顺序

默认情况下,Grails会对动态脚手架生成的视图中的字段进行字母排序。若想指定字段顺序,可在领域类中创建一个静态 constraints 块:

class Publisher{
    static constraints = {
        name()
        address()
        city()
        state()
        zipcode()
    }
    String name
    String address
    String city
    String state
    String zipcode
    String toString(){
        return name
    }
}

虽然这种方式可能不符合DRY原则,但 constraints 块的用途不仅限于字段排序,在数据验证等方面也有应用。

综上所述,Grails为开发者提供了丰富的功能和灵活的配置选项,通过合理运用这些特性,我们可以高效地开发出高质量的Web应用。无论是环境管理、数据库配置、页面定制还是控制器和视图的处理,Grails都有相应的解决方案,帮助开发者更轻松地完成开发任务。

9. 数据验证

在Grails应用中,数据验证是确保数据完整性和正确性的重要环节。我们可以在领域类中使用 constraints 块来定义验证规则。以下是一个示例:

class Book {
    String title
    String author
    Integer pages

    static constraints = {
        title blank: false, maxSize: 100
        author blank: false, maxSize: 50
        pages nullable: true, min: 1
    }
}

在上述代码中,我们为 Book 类的 title author pages 属性定义了验证规则:
- title :不能为空,最大长度为100。
- author :不能为空,最大长度为50。
- pages :可以为空,最小值为1。

当我们创建或更新 Book 对象时,Grails会自动根据这些规则进行验证。如果验证失败,对象的 hasErrors() 方法将返回 true ,我们可以通过 errors 属性获取详细的错误信息。以下是一个使用示例:

def save = {
    def book = new Book(params)
    if (book.validate()) {
        book.save()
        flash.message = "Book saved successfully"
        redirect(action: 'show', id: book.id)
    } else {
        render(view: 'create', model: [book: book])
    }
}

save 动作中,我们首先创建一个新的 Book 对象,然后调用 validate() 方法进行验证。如果验证通过,将对象保存到数据库并跳转到显示页面;如果验证失败,将渲染 create 视图并显示错误信息。

10. 拦截器

拦截器是Grails中一种强大的机制,用于在控制器动作执行前后进行额外的处理。我们可以创建一个拦截器类,并在其中定义拦截规则。以下是一个示例:

class BookInterceptor {
    boolean before() { true }
    boolean after() { true }
    void afterView() {}

    BookInterceptor() {
        match(controller: 'book')
    }
}

在上述代码中,我们创建了一个名为 BookInterceptor 的拦截器类。 before() 方法在控制器动作执行前调用, after() 方法在控制器动作执行后调用, afterView() 方法在视图渲染后调用。 match() 方法用于指定拦截的控制器。

我们还可以在拦截器中进行更复杂的处理,例如身份验证、日志记录等。以下是一个添加身份验证的示例:

class AuthInterceptor {
    boolean before() {
        if (!session.user) {
            flash.message = "Please log in"
            redirect(controller: 'login', action: 'index')
            return false
        }
        return true
    }
    boolean after() { true }
    void afterView() {}

    AuthInterceptor() {
        matchAll()
    }
}

AuthInterceptor 中,我们在 before() 方法中检查用户是否已登录。如果未登录,将重定向到登录页面并返回 false ,阻止控制器动作的执行。

11. 过滤器

过滤器与拦截器类似,也是用于在请求处理的不同阶段进行额外的处理。不同的是,过滤器是基于Servlet规范的,它可以在请求到达控制器之前或响应返回给客户端之前进行处理。我们可以在 grails-app/conf/UrlMappings.groovy 文件中定义过滤器。以下是一个示例:

class UrlMappings {
    static mappings = {
        "/**"(filters: 'myFilter')
    }
}

在上述代码中,我们使用 filters 属性指定了一个名为 myFilter 的过滤器,该过滤器将应用于所有请求。

我们还需要创建一个过滤器类来实现具体的过滤逻辑。以下是一个简单的日志过滤器示例:

import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.FilterConfig
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse

class LoggingFilter implements Filter {
    void init(FilterConfig filterConfig) {}

    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        println "Request received: ${request.getRequestURI()}"
        chain.doFilter(request, response)
        println "Response sent"
    }

    void destroy() {}
}

LoggingFilter 中,我们实现了 Filter 接口,并在 doFilter() 方法中记录请求和响应的信息。

12. 缓存

缓存是提高应用性能的重要手段之一。Grails支持多种缓存机制,包括内存缓存、分布式缓存等。我们可以在 grails-app/conf/DataSource.groovy 文件中配置缓存。以下是一个使用EhCache的示例:

hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'org.hibernate.cache.EhCacheProvider'
}

在上述代码中,我们启用了二级缓存和查询缓存,并指定了缓存提供者为 EhCacheProvider

我们还可以在控制器动作中使用缓存注解来缓存方法的返回值。以下是一个示例:

import grails.plugin.cache.Cacheable

class BookController {
    @Cacheable('bookList')
    def list = {
        if (!params.max) params.max = 10
        [bookList: Book.list(params)]
    }
}

list 动作上使用 @Cacheable 注解,指定缓存名称为 bookList 。当该动作被调用时,Grails会首先检查缓存中是否存在结果,如果存在则直接返回缓存中的结果,否则执行方法并将结果存入缓存。

13. 国际化

在多语言环境下,我们需要对应用进行国际化处理。Grails提供了丰富的支持来实现这一功能。我们可以在 grails-app/i18n 目录下创建不同语言的属性文件,例如 messages.properties (默认语言)、 messages_en.properties (英语)、 messages_zh.properties (中文)等。以下是一个示例:

# messages.properties
book.title=Title
book.author=Author
book.pages=Pages
# messages_zh.properties
book.title=标题
book.author=作者
book.pages=页数

在视图中,我们可以使用 g:message 标签来获取国际化的文本。以下是一个示例:

<g:message code="book.title" />

在控制器中,我们可以通过 Locale 对象来设置当前语言。以下是一个示例:

def changeLocale = {
    session.locale = new Locale(params.lang)
    redirect(action: 'index')
}

在上述代码中,我们通过 session.locale 设置当前语言,并根据用户选择的语言重定向到首页。

14. 插件

Grails拥有丰富的插件生态系统,这些插件可以帮助我们快速实现各种功能。我们可以在 BuildConfig.groovy 文件中添加插件依赖。以下是一个添加 mail 插件的示例:

grails.plugin.location.'mail' = 'https://grails.org/plugins/grails-mail'
dependencies {
    // other dependencies
}
plugins {
    runtime ":mail:1.0.1"
}

在上述代码中,我们指定了 mail 插件的下载地址,并在 plugins 块中添加了插件依赖。

安装插件后,我们可以在应用中使用插件提供的功能。以 mail 插件为例,我们可以在控制器中发送邮件:

import grails.plugin.mail.MailService

class ContactController {
    MailService mailService

    def sendMail = {
        mailService.sendMail {
            to "recipient@example.com"
            from "sender@example.com"
            subject "Test Email"
            body "This is a test email"
        }
        flash.message = "Email sent successfully"
        redirect(action: 'index')
    }
}

在上述代码中,我们注入了 MailService ,并在 sendMail 动作中使用它来发送邮件。

15. 总结

通过以上内容,我们全面了解了Grails开发中的各个方面,包括环境配置、数据库操作、控制器和视图处理、动态脚手架、数据验证、拦截器、过滤器、缓存、国际化和插件等。Grails提供了丰富的功能和灵活的配置选项,使得我们可以高效地开发出高质量的Web应用。

在实际开发中,我们可以根据项目的需求选择合适的技术和工具,合理运用Grails的各种特性,提高开发效率和应用性能。同时,我们还可以通过不断学习和实践,深入掌握Grails的高级特性,为项目的成功实施提供有力的支持。

以下是Grails开发的主要流程总结:
1. 环境配置:设置Grails环境、端口、数据库等。
2. 领域类定义:创建领域类并定义属性和验证规则。
3. 控制器和视图开发:实现控制器动作和对应的视图。
4. 动态脚手架使用:快速生成控制器和视图。
5. 拦截器和过滤器应用:进行请求处理和额外的处理。
6. 缓存和国际化处理:提高应用性能和支持多语言。
7. 插件使用:利用插件扩展应用功能。

mermaid flowchart LR
A[环境配置] –> B[领域类定义]
B –> C[控制器和视图开发]
C –> D[动态脚手架使用]
D –> E[拦截器和过滤器应用]
E –> F[缓存和国际化处理]
F –> G[插件使用]

通过遵循以上流程,我们可以有条不紊地进行Grails项目的开发,确保项目的顺利进行。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值