(Quick Reference)

3 配置 - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith

Version: null

3 配置

It may seem odd that in a framework that embraces "convention-over-configuration" that we tackle this topic now, but since what configuration there is typically a one-off, it is best to get it out the way.

With Grails' default settings you can actually develop an application without doing any configuration whatsoever. Grails ships with an embedded servlet container and in-memory H2 database, so there isn't even a database to set up.

However, typically you should configure a more robust database at some point and that is described in the following section.

也许在这里谈论配置对于一个遵循“规约优于配置”的框架来说,会让人感到比较奇怪,但是实际上我们这里所说的配置是两个不同的概念,请不要混淆。

实际上Grails的默认配置已经足以我们进行开发,并且它内置了容器和内存模式的H2数据库,这样我们几乎连数据库都不用配置了。

不过,在将来你肯定是想要配置一个真正的数据库的,下面的章节将介绍如何实现。

3.1 基本配置

For general configuration Grails provides a file called grails-app/conf/Config.groovy. This file uses Groovy's ConfigSlurper which is very similar to Java properties files except it is pure Groovy hence you can reuse variables and use proper Java types!

You can add your own configuration in here, for example:

foo.bar.hello = "world"

Then later in your application you can access these settings in one of two ways. The most common is from the GrailsApplication object, which is available as a variable in controllers and tag libraries:

assert "world" == grailsApplication.config.foo.bar.hello

The other way involves getting a reference to the ConfigurationHolder class that holds a reference to the configuration object:

import org.codehaus.groovy.grails.commons.*
…
def config = ConfigurationHolder.config
assert "world" == config.foo.bar.hello

ConfigurationHolder and ApplicationHolder are deprecated and will be removed in a future version of Grails, so it is highly preferable to access the GrailsApplication and config from the grailsApplication variable.

Grails提供了一个 grails-app/conf/Config.groovy 配置文件,用来完成通用的配置。此文件除了是Groovy的ConfigSlurper 之外,其他非常类似于Java属性文件,这样就既可以重用变量又可以使用合适的Java类!

你可以添加属于你自己的配置信息,例如:

foo.bar.hello = "world"

在你随后的应用中,你可以使用以下两种方法中的一种来访问这些配置。最常用的就是 GrailsApplication 对象,不过此对象仅在控制器和标签库中有效。比如:

assert "world" == grailsApplication.config.foo.bar.hello

另外一种方法是获取 ConfigurationHolder 类的一个引用,此类中包含着配置对象,比如:

import org.codehaus.groovy.grails.commons.*
…
def config = ConfigurationHolder.config
assert "world" == config.foo.bar.hello

ConfigurationHolder和ApplicationHolder现在已经被废弃,并且将在Grails的未来版本中移除,因此强烈推荐采用 GrailsApplication 的实例变量 grailsApplication 方式来访问配置对象。

3.1.1 内置选项

Grails also provides the following configuration options:
  • grails.config.locations - The location of properties files or addition Grails Config files that should be merged with main configuration
  • grails.enable.native2ascii - Set this to false if you do not require native2ascii conversion of Grails i18n properties files
  • grails.views.default.codec - Sets the default encoding regime for GSPs - can be one of 'none', 'html', or 'base64' (default: 'none'). To reduce risk of XSS attacks, set this to 'html'.
  • grails.views.gsp.encoding - The file encoding used for GSP source files (default is 'utf-8')
  • grails.mime.file.extensions - Whether to use the file extension to dictate the mime type in Content Negotiation
  • grails.mime.types - A map of supported mime types used for Content Negotiation
  • grails.serverURL - A string specifying the server URL portion of absolute links, including server name e.g. grails.serverURL="http://my.yourportal.com". See createLink.

Grails同样提供了如下配置选项:

  • grails.config.locations - 配置文件的位置,包括属性文件或者其他需要合并到主配置的Grails配置文件
  • grails.enable.native2ascii - 如果不需要native2ascii来转化Grails i18n属性文件的话,将该选项设为false
  • grails.views.default.codec - 设置GSP的默认编码制式,可以是:'none', 'html', 或者 'base64' (缺省为'none'). 为了减少XSS攻击的风险,建议设置成'html'.
  • grails.views.gsp.encoding - GSP源文件的字符编码(缺省是'utf-8')
  • grails.mime.file.extensions - 是否使用文件的扩展名表示内容协商中的媒体类型(mime type)
  • grails.mime.types - 内容协商所支持的媒体类型
  • grails.serverURL - 一个指向服务器URL的绝对地址,包括服务器名称,比如grails.serverURL="http://my.yourportal.com". 详细请看createLink

War generation

  • grails.project.war.file - Sets the name and location of the WAR file generated by the war command
  • grails.war.dependencies - A closure containing Ant builder syntax or a list of JAR filenames. Lets you customise what libaries are included in the WAR file.
  • grails.war.copyToWebApp - A closure containing Ant builder syntax that is legal inside an Ant copy, for example "fileset()". Lets you control what gets included in the WAR file from the "web-app" directory.
  • grails.war.resources - A closure containing Ant builder syntax. Allows the application to do any other other work before building the final WAR file

For more information on using these options, see the section on deployment

War生成选项

  • grails.project.war.file - 设置 war 命令生成WAR文件的名称和位置
  • grails.war.dependencies - 符合Ant生成器语法的闭包或者JAR文件的列表,让你可以定制WAR文件所需要的依赖库。
  • grails.war.copyToWebApp - 完成Ant拷贝且满足其生成器语法的闭包,比如"fileset()"。让你控制"web-app"目录下那些资源可以被打包到WAR文件中。
  • grails.war.resources - 符合Ant生成器语法的闭包,运行应用在构建最终的WAR文件前做任何其他的预处理

这些选项的更多信息,请参考 部署 章节。

3.1.2 日志

The Basics

Grails uses its common configuration mechanism to provide the settings for the underlying Log4j log system, so all you have to do is add a log4j setting to the file grails-app/conf/Config.groovy.

So what does this log4j setting look like? Here's a basic example:

log4j = {
    error  'org.codehaus.groovy.grails.web.servlet',  //  controllers
           'org.codehaus.groovy.grails.web.pages' //  GSP

warn 'org.apache.catalina' }

This says that for loggers whose name starts with 'org.codehaus.groovy.grails.web.servlet' or 'org.codehaus.groovy.grails.web.pages', only messages logged at 'error' level and above will be shown. Loggers with names starting with 'org.apache.catalina' logger only show messages at the 'warn' level and above. What does that mean? First of all, you have to understand how levels work.

基础

Grails利用其自身的配置机制来提供对 Log4j 日志系统的配置,因此你所需要做的只是将log4j配置添加到grails-app/conf/Config.groovy配置文件中。

那么log4j该配置什么样子呢?下边是一个基础的示例:

log4j = {
    error  'org.codehaus.groovy.grails.web.servlet',  //  controllers
           'org.codehaus.groovy.grails.web.pages' //  GSP

warn 'org.apache.catalina' }

在上述示例中,那些名称以'org.codehaus.groovy.grails.web.servlet'或者'org.codehaus.groovy.grails.web.pages'开头的记录器,仅仅记录级别高于或等于'error'的信息;而名字以'org.apache.catalina'开始的记录器仅仅记录级别高于或等于'warn'的信息。要了解此中的意义,首先要知道日志级别是如何工作的。

Logging levels

The are several standard logging levels, which are listed here in order of descending priority:

  1. off
  2. fatal
  3. error
  4. warn
  5. info
  6. debug
  7. trace
  8. all

When you log a message, you implicitly give that message a level. For example, the method log.error(msg) will log a message at the 'error' level. Likewise, log.debug(msg) will log it at 'debug'. Each of the above levels apart from 'off' and 'all' have a corresponding log method of the same name.

The logging system uses that message level combined with the configuration for the logger (see next section) to determine whether the message gets written out. For example, if you have an 'org.example.domain' logger configured like so:

warn 'org.example.domain'

then messages with a level of 'warn', 'error', or 'fatal' will be written out. Messages at other levels will be ignored.

Before we go on to loggers, a quick note about those 'off' and 'all' levels. These are special in that they can only be used in the configuration; you can't log messages at these levels. So if you configure a logger with a level of 'off', then no messages will be written out. A level of 'all' means that you will see all messages. Simple.

日志级别

以下是按照优先级降序(由高到低)排列的标准日志级别:

  1. off
  2. fatal
  3. error
  4. warn
  5. info
  6. debug
  7. trace
  8. all

当你记录一条消息的时候,已经暗含地给此消息指定了级别,比如log.error(msg)方法,就是使用其'error'级别,同理log.debug(msg)指定的是'debug'。上述从'off'到'all'的级别都有一个同名对应的日志方法。

日志系统使用记录器(介绍见下一节)配置的 message 级别来判断此消息是否应该输出,比如你有一个'org.example.domain'记录器,其配置如下:

warn 'org.example.domain'

那么级别是'warn'、'error'或者'fatal'的消息都将会输出,而其他级别的都将被忽略。

在我们继续记录器以前,我们需要对'off'和'all'的级别做一个小关注,它们都只能在配置中使用,你不能使用它们记录任何日志信息。因此如果你将级别配置为'off',那么将不会有任何信息输出,而配置为'all'意味着你将看到所有的日志信息。

Loggers

Loggers are fundamental to the logging system, but they are a source of some confusion. For a start, what are they? Are they shared? How do you configure them?

A logger is the object you log messages to, so in the call log.debug(msg), log is a logger instance (of type Log). These loggers are cached and uniquely identified by name, so if two separate classes use loggers with the same name, those loggers are actually the same instance.

There are two main ways to get hold of a logger:

  1. use the log instance injected into artifacts such as domain classes, controllers and services;
  2. use the Commons Logging API directly.

If you use the dynamic log property, then the name of the logger is 'grails.app.<type>.<className>', where type is the type of the artifact, for example 'controller' or 'service, and className is the fully qualified name of the artifact. For example, if you have this service:

package org.example

class MyService { … }

then the name of the logger will be 'grails.app.services.org.example.MyService'.

For other classes, the typical approach is to store a logger based on the class name in a constant static field:

package org.other

import org.apache.commons.logging.LogFactory

class MyClass { private static final log = LogFactory.getLog(this) … }

This will create a logger with the name 'org.other.MyClass' - note the lack of a 'grails.app.' prefix since the class isn't an artifact. You can also pass a name to the getLog() method, such as "myLogger", but this is less common because the logging system treats names with dots ('.') in a special way.

记录器(Loggers)

记录器是日志系统的基础,但是依然有一些根源上的困惑,比如它们是什么?可否共享?以及如何配置它们?

一个记录器就是你要将信息记录进去的对象,因此log.debug(msg)中的log就是一个记录器实例(其类型是Log). 这些记录器通过唯一的名字标识被缓存起来,因此如果两个不同的类使用同一个名字的记录器,那么这些记录器是同一个运行实例。

主要有两种方法来获取一个记录器:

  1. 使用注入到工件(比如领域类、控制器以及服务)中的log实例
  2. 直接使用Commons Logging API。

如果你使用动态的log属性,那么记录器的名字是'grails.app.<type>.<className>',此处的type是工件的类型,比如'controller'或者'service',而className则是此工件的全名,假设你有如下的一个服务:

package org.example

class MyService { … }

那么上述记录器的名字是'grails.app.services.org.example.MyService'。

对其他类来说,典型的方法是将记录器作为此类的一个静态常量字段,比如:

package org.other

import org.apache.commons.logging.LogFactory

class MyClass { private static final log = LogFactory.getLog(this) … }

上述代码中将创建一个名字为'org.other.MyClass'的记录器,注意:此处并没有'grails.app.'前缀,因为此类不是一个Grails工件。你还可以一个自定义的名字(比如"myLogger")给getLog()方法,但是这种用法并不常见,因为在日志系统中,名字中的点('.')是被特殊处理的。

Configuring loggers

You have already seen how to configure loggers in Grails:

log4j = {
    error  'org.codehaus.groovy.grails.web.servlet'
}

This example configures loggers with names starting with 'org.codehaus.groovy.grails.web.servlet' to ignore any messages sent to them at a level of 'warn' or lower. But is there a logger with this name in the application? No. So why have a configuration for it? Because the above rule applies to any logger whose name begins with 'org.codehaus.groovy.grails.servlet.' as well. For example, the rule applies to both the org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet class and the org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest one.

In other words, loggers are hierarchical. This makes configuring them by package much simpler than it would otherwise be.

The most common things that you will want to capture log output from are your controllers, services, and other artifacts. Use the convention mentioned earlier to do that: grails.app.<artifactType>.<className> . In particular the class name must be fully qualifed, i.e. with the package if there is one:

log4j = {
    // Set level for all application artifacts
    info "grails.app"

// Set for a specific controller in the default package debug "grails.app.controllers.YourController"

// Set for a specific domain class debug "grails.app.domain.org.example.Book"

// Set for all taglibs info "grails.app.taglib" }

The standard artifact names used in the logging configuration are:

  • conf - For anything under grails-app/conf such as BootStrap.groovy (but excluding filters)
  • filters - For filters
  • taglib - For tag libraries
  • services - For service classes
  • controllers - For controllers
  • domain - For domain entities

Grails itself generates plenty of logging information and it can sometimes be helpful to see that. Here are some useful loggers from Grails internals that you can use, especially when tracking down problems with your application:

  • org.codehaus.groovy.grails.commons - Core artifact information such as class loading etc.
  • org.codehaus.groovy.grails.web - Grails web request processing
  • org.codehaus.groovy.grails.web.mapping - URL mapping debugging
  • org.codehaus.groovy.grails.plugins - Log plugin activity
  • grails.spring - See what Spring beans Grails and plugins are defining
  • org.springframework - See what Spring is doing
  • org.hibernate - See what Hibernate is doing

So far, we've only looked at explicit configuration of loggers. But what about all those loggers that don't have an explicit configuration? Are they simply ignored? The answer lies with the root logger.

配置记录器

你已在Grails中看到如何配置记录器了,比如:

log4j = {
    error  'org.codehaus.groovy.grails.web.servlet'
}

此示例中,名字以'org.codehaus.groovy.grails.web.servlet'开始的记录器将忽略所有'warn'级别以下的消息。打住,在你的应用中真有此名字的记录器么?没有,那我们为什么要这样配置它呢?因为上述的规则将应用于任何以'org.codehaus.groovy.grails.servlet.'_开始_的记录器,比如类org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServletorg.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest

换句话说,记录器是分层级的,这使得用包名来配置比其他方式容易很多。

在应用中,你最常记录的是控制器、服务以及其他工件的输出日志,这可以通过以前提到过的_grails.app.<artifactType>.<className>_来实现。需要注意的是类名必须是全名(包括包名),如下所示:

log4j = {
    // Set level for all application artifacts
    info "grails.app"

// Set for a specific controller in the default package debug "grails.app.controllers.YourController"

// Set for a specific domain class debug "grails.app.domain.org.example.Book"

// Set for all taglibs info "grails.app.taglib" }

在日志配置中,常用的标准工件名称如下:

  • conf - grails-app/conf下的任何类(过滤器除外),比如:BootStrap.groovy
  • filters - 过滤器
  • taglib - 标签库
  • services - 服务类
  • controllers - 控制器
  • domain - 领域类

Grails本身也带有大量的日志信息,有时候这些信息对我们的开发很有裨益,尤其要诊断你应用的问题的时候。以下是一些你可能使用到的内部记录器:

  • org.codehaus.groovy.grails.commons - 核心工件信息,比如类加载等
  • org.codehaus.groovy.grails.web - Grails的web请求处理
  • org.codehaus.groovy.grails.web.mapping - 调试URL映射信息
  • org.codehaus.groovy.grails.plugins - 记录插件的活动情况
  • grails.spring - 在Grails和插件中定义的Spring的beans
  • org.springframework - Spring的活动情况
  • org.hibernate - Hibernate的活动情况

到目前为止,我们仅仅查看了记录器的显式配置,那么其他那些_没有_明确指定的将是什么情况呢?它们是被简单的忽略了么?请看下面的根记录器小节。

The Root Logger

All logger objects inherit their configuration from the root logger, so if no explicit configuration is provided for a given logger, then any messages that go to that logger are subject to the rules defined for the root logger. In other words, the root logger provides the default configuration for the logging system.

Grails automatically configures the root logger to only handle messages at 'error' level and above, and all the messages are directed to the console (stdout for those with a C background). You can customise this behaviour by specifying a 'root' section in your logging configuration like so:

log4j = {
    root {
        info()
    }
    …
}

The above example configures the root logger to log messages at 'info' level and above to the default console appender. You can also configure the root logger to log to one or more named appenders (which we'll talk more about shortly):

log4j = {
    appenders {
        file name:'file', file:'/var/logs/mylog.log'
    }
    root {
        debug 'stdout', 'file'
    }
}

In the above example, the root logger will log to two appenders - the default 'stdout' (console) appender and a custom 'file' appender.

For power users there is an alternative syntax for configuring the root logger: the root org.apache.log4j.Logger instance is passed as an argument to the log4j closure. This lets you work with the logger directly:

log4j = { root ->
    root.level = org.apache.log4j.Level.DEBUG
    …
}

For more information on what you can do with this Logger instance, refer to the Log4j API documentation.

Those are the basics of logging pretty well covered and they are sufficient if you're happy to only send log messages to the console. But what if you want to send them to a file? How do you make sure that messages from a particular logger go to a file but not the console? These questions and more will be answered as we look into appenders.

根记录器

所有的记录器对象配置都是从其根记录器继承而来的,因此一个记录器如果没有明确地配置,那么此记录器的任何消息规则都使用其根记录器的定义。或者说,根记录器提供日志系统的缺省配置。

Grails自动地将根记录器配置成只处理'error'级别地消息,并且将这些消息显示在命令行终端(stdout是从C语言中借鉴而来)中。你可以通过'root'来重新定义其行为,比如:

log4j = {
    root {
        info()
    }
    …
}

上述示例将配置根记录器记录并且输出'info'级别的消息到缺省的字符输出器。你也可以配置根记录器将信息记录到一个或者多个输出器(在下小节中详细讨论)中。比如:

log4j = {
    appenders {
        file name:'file', file:'/var/logs/mylog.log'
    }
    root {
        debug 'stdout', 'file'
    }
}

在上述示例中,根记录器将记录到两个输出器中:缺省的'stdout'输出器和自定义的'file'输出器。

对高级用户来说,还有另外一种配置根记录器的方式:传递给log4j闭包的参数root是org.apache.log4j.Logger实例,这让你可以直接操作logger:

log4j = { root ->
    root.level = org.apache.log4j.Level.DEBUG
    …
}

更多Logger实例的信息,请参考Log4j API文档。

如果你仅仅满足于将日志信息输出到字符终端,那么目前所涉及到的基本信息已经足够用的了。但是如果你还想输出到一个文件呢?以及想将特定记录器的信息输出到一个特定文件,而不是字符终端,又该如何做呢?这些疑问将在下一节的输出器中得到解答。

Appenders

Loggers are a useful mechanism for filtering messages, but they don't physically write the messages anywhere. That's the job of the appender, of which there are various types. For example, there is the default one that writes messages to the console, another that writes them to a file, and several others. You can even create your own appender implementations!

This diagram shows how they fit into the logging pipeline:

As you can see, a single logger may have several appenders attached to it. In a standard Grails configuration, the console appender named 'stdout' is attached to all loggers through the default root logger configuration. But that's the only one. Adding more appenders can be done within an 'appenders' block:

log4j = {
    appenders {
        rollingFile name: "myAppender",
                    maxFileSize: 1024,
                    file: "/tmp/logs/myApp.log"
    }
}

The following appenders are available by default:

NameClassDescription
jdbcJDBCAppenderLogs to a JDBC connection.
consoleConsoleAppenderLogs to the console.
fileFileAppenderLogs to a single file.
rollingFileRollingFileAppenderLogs to rolling files, for example a new file each day.

Each named argument passed to an appender maps to a property of the underlying Appender implementation. So the previous example sets the name, maxFileSize and file properties of the RollingFileAppender instance.

You can have as many appenders as you like - just make sure that they all have unique names. You can even have multiple instances of the same appender type, for example several file appenders that log to different files.

If you prefer to create the appender programmatically or if you want to use an appender implementation that's not available in the above syntax, simply declare an appender entry with an instance of the appender you want:

import org.apache.log4j.*

log4j = { appenders { appender new RollingFileAppender( name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log") } }

This approach can be used to configure JMSAppender, SocketAppender, SMTPAppender, and more.

Once you have declared your extra appenders, you can attach them to specific loggers by passing the name as a key to one of the log level methods from the previous section:

error myAppender: "grails.app.controllers.BookController"

This will ensure that the 'grails.app.controllers.BookController' logger sends log messages to 'myAppender' as well as any appenders configured for the root logger. To add more than one appender to the logger, then add them to the same level declaration:

error myAppender:      "grails.app.controllers.BookController",
      myFileAppender:  ["grails.app.controllers.BookController",
                        "grails.app.services.BookService"],
      rollingFile:     "grails.app.controllers.BookController"

The above example also shows how you can configure more than one logger at a time for a given appender (myFileAppender) by using a list.

Be aware that you can only configure a single level for a logger, so if you tried this code:

error myAppender:      "grails.app.controllers.BookController"
debug myFileAppender:  "grails.app.controllers.BookController"
fatal rollingFile:     "grails.app.controllers.BookController"

you'd find that only 'fatal' level messages get logged for 'grails.app.controllers.BookController'. That's because the last level declared for a given logger wins. What you probably want to do is limit what level of messages an appender writes.

An appender that is attached to a logger configured with the 'all' level will generate a lot of logging information. That may be fine in a file, but it makes working at the console difficult. So we configure the console appender to only write out messages at 'info' level or above:

log4j = {
    appenders {
        console name: "stdout", threshold: org.apache.log4j.Level.INFO
    }
}

The key here is the threshold argument which determines the cut-off for log messages. This argument is available for all appenders, but do note that you currently have to specify a Level instance - a string such as "info" will not work.

输出器(Appenders)

记录器是很好的信息过滤机制,但是它们并不将信息进行任何物理的写操作,这些都是不同类型的输出器所做的事。比如缺省的一个就是将信息输出到字符终端,另外一个输出到一个文件等等,更有甚者,你还可以创建你自己的输出器!

下图展示了输出器在日志管道系统中的位置:

如你所见,一个记录器可以挂载多个输出器。 在一个标准的Grails配置中,所有从根记录器而来的记录器都有一个名为'stdout'并唯一的字符终端输出器。你可以通过'appenders'代码块来增加更多的输出器,比如:

log4j = {
    appenders {
        rollingFile name: "myAppender",
                    maxFileSize: 1024,
                    file: "/tmp/logs/myApp.log"
    }
}

以下是缺省情况下,有效输出器的清单:

名称类名描述
jdbcJDBCAppender记录到JDBC连接。
consoleConsoleAppender记录到字符终端。
fileFileAppender记录到一个文件。
rollingFileRollingFileAppender记录到滚动文件,比如一天一个新文件。

传递给输出器的每一个命名参数都将映射成实现了Appender接口的属性,因此上述的RollingFileAppender的实例中,namemaxFileSizefile都是其属性而已。

你可以添加任意你需要的输出器,只要确保它们的名字不重复就可以了。同一类型的输出器,你甚至还可以有多个实例,比如将日志内容输出到不同的文件中。

你如果倾向于手工创建输出器或者你需要的输出器不在上述的列表中,那么你只需简单的声明一个appender代码即可,比如:

import org.apache.log4j.*

log4j = { appenders { appender new RollingFileAppender( name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log") } }

此种方法通常用来配置JMSAppenderSocketAppenderSMTPAppender等输出器。

一旦你声明了这些额外的输出器,那么你还需要将它们跟特定的记录器进行关联,这可以通过记录器的名称和记录级别来完成,比如:

error myAppender: "grails.app.controllers.BookController"

这样就可以保证记录器'grails.app.controllers.BookController'将消息发送到'myAppender'中以及配置在根记录器中的任何输出器,要在记录器中增加更多的输出器,只需要将他们加入到同级别的声明即可,比如:

error myAppender:      "grails.app.controllers.BookController",
      myFileAppender:  ["grails.app.controllers.BookController",
                        "grails.app.services.BookService"],
      rollingFile:     "grails.app.controllers.BookController"

上述示例同时也展示了在一个给定的输出器(myFileAppender)中如何通过列表来配置多个记录器。

需要注意的是:一个记录器只能配置一个级别,如果你配置了如下的内容:

error myAppender:      "grails.app.controllers.BookController"
debug myFileAppender:  "grails.app.controllers.BookController"
fatal rollingFile:     "grails.app.controllers.BookController"

你将会发现'grails.app.controllers.BookController'记录器只记录'fatal'级别的消息,这是因为最后的级别设置将以前的覆盖掉了。你这样做的意图是想限制输出器的级别。

一个根'all'级别记录器关联的输出器将记录大量的日志信息,如果记录在文件中,也许还能忍受,但在字符终端完全是另外一回事。因此我们需要将字符终端的输出器只记录'info'及其级别以上内容:

log4j = {
    appenders {
        console name: "stdout", threshold: org.apache.log4j.Level.INFO
    }
}

此处的threshold参数用以判断那些消息需要截去。此参数对所有的输出器有效,但需要注意的是你必须使用Level实例-"info"字符串的便利用法不能工作。

Custom Layouts

By default the Log4j DSL assumes that you want to use a PatternLayout. However, there are other layouts available including:

  • xml - Create an XML log file
  • html - Creates an HTML log file
  • simple - A simple textual log
  • pattern - A Pattern layout

You can specify custom patterns to an appender using the layout setting:

log4j = {
    appenders {
        console name: "customAppender",
                layout: pattern(conversionPattern: "%c{2} %m%n")
    }
}

This also works for the built-in appender "stdout", which logs to the console:

log4j = {
    appenders {
        console name: "stdout",
                layout: pattern(conversionPattern: "%c{2} %m%n")
    }
}

自定义布局

多数情况下,Log4j DSL使用缺省的PatternLayout,除此之外,还有以下布局可以选择:

  • xml - 创建一个XML日志文件
  • html - 创建一个HTML日志文件
  • simple - 简单的文本文件
  • pattern - Pattern布局的文件

你可以通过layout来给一个输出器自定义布局:

log4j = {
    appenders {
        console name: "customAppender",
                layout: pattern(conversionPattern: "%c{2} %m%n")
    }
}

此配置对内置的"stdout"(输出到字符终端)也有效:

log4j = {
    appenders {
        console name: "stdout",
                layout: pattern(conversionPattern: "%c{2} %m%n")
    }
}

Environment-specific configuration

Since the logging configuration is inside Config.groovy, you can put it inside an environment-specific block. However, there is a problem with this approach: you have to provide the full logging configuration each time you define the log4j setting. In other words, you cannot selectively override parts of the configuration - it's all or nothing.

To get around this, the logging DSL provides its own environment blocks that you can put anywhere in the configuration:

log4j = {
    appenders {
        console name: "stdout",
                layout: pattern(conversionPattern: "%c{2} %m%n")

environments { production { rollingFile name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log" } } }

root { //… }

// other shared config info "grails.app.controllers"

environments { production { // Override previous setting for 'grails.app.controllers' error "grails.app.controllers" } } }

The one place you can't put an environment block is inside the root definition, but you can put the root definition inside an environment block.

特定环境的配置

既然日志是配置在Config.groovy中,你自然也就可以将其配置在环境相关的代码块中。不过这种方式有一个小问题:每次你配置log4j的时候,你必须提供完整的日志配置。或者换句话说,你不能选择性的覆盖部分配置-要么全覆盖要么一点也不覆盖。

为了避免此问题,日志DSL提供了自己的environment代码块配置,这样你就可以自由的配置了。

log4j = {
    appenders {
        console name: "stdout",
                layout: pattern(conversionPattern: "%c{2} %m%n")

environments { production { rollingFile name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log" } } }

root { //… }

// other shared config info "grails.app.controllers"

environments { production { // Override previous setting for 'grails.app.controllers' error "grails.app.controllers" } } }

此处需要注意的是:你不能将environment代码块放在root定义的_内部_,但是你可以将其放在environment代码块中。

Full stacktraces

When exceptions occur, there can be an awful lot of noise in the stacktrace from Java and Groovy internals. Grails filters these typically irrelevant details and restricts traces to non-core Grails/Groovy class packages.

When this happens, the full trace is always logged to the StackTrace logger, which by default writes its output to a file called stacktrace.log. As with other loggers though, you can change its behaviour in the configuration. For example if you prefer full stack traces to go to the console, add this entry:

error stdout: "StackTrace"

This won't stop Grails from attempting to create the stacktrace.log file - it just redirects where stack traces are written to. An alternative approach is to change the location of the 'stacktrace' appender's file:

log4j = {
    appenders {
        rollingFile name: "stacktrace", maxFileSize: 1024,
                    file: "/var/tmp/logs/myApp-stacktrace.log"
    }
}

or, if you don't want to the 'stacktrace' appender at all, configure it as a 'null' appender:

log4j = {
    appenders {
        'null' name: "stacktrace"
    }
}

You can of course combine this with attaching the 'stdout' appender to the 'StackTrace' logger if you want all the output in the console.

Finally, you can completely disable stacktrace filtering by setting the grails.full.stacktrace VM property to true:

grails -Dgrails.full.stacktrace=true run-app

完整的栈跟踪

当一个异常发生时,可能有大量的来自Java和Groovy内部栈跟踪信息,这其实是很恼人的。Grails的过滤器将这些不相干的细节屏蔽了,并且将栈的跟踪信息限制非Grails/Groovy的类包范围。

当异常发生时,完整的跟踪信息总是被记录到StackTrace记录器中,此记录器缺省将内容输出到一个名为stacktrace.log的文件中。跟其他的记录器配合,你还可以改变其在配置中的行为,比如你可以将栈跟踪信息输出到字符终端:

error stdout: "StackTrace"

此动作不会阻止Grails创建stacktrace.log文件-它只是将栈跟踪重定向了而已。此外你还可以修改'stacktrace'输出器的位置信息,比如:

log4j = {
    appenders {
        rollingFile name: "stacktrace", maxFileSize: 1024,
                    file: "/var/tmp/logs/myApp-stacktrace.log"
    }
}

或者,你根本就不想输出'stacktrace',只需要将其输出器配置为'null'即可:

log4j = {
    appenders {
        'null' name: "stacktrace"
    }
}

你如果想在字符终端看到所有的输出,你可以通过将'StackTrace'记录器和'stdout'输出器关联合并来实现。

最后,你如果想完全禁止栈跟踪的过滤,可以通过设置VM属性:grails.full.stacktracetrue来实现,比如:

grails -Dgrails.full.stacktrace=true run-app

Masking Request Parameters From Stacktrace Logs

When Grails logs a stacktrace, the log message may include the names and values of all of the request parameters for the current request. To mask out the values of secure request parameters, specify the parameter names in the grails.exceptionresolver.params.exclude config property:

grails.exceptionresolver.params.exclude = ['password', 'creditCard']

Request parameter logging may be turned off altogether by setting the grails.exceptionresolver.logRequestParameters config property to false. The default value is true when the application is running in DEVELOPMENT mode and false for all other modes.

grails.exceptionresolver.logRequestParameters=false

屏蔽栈跟踪日志中的请求参数

当Grails记录栈跟踪信息的时候,有可能将当前请求参数的名称和值一并包含了。为了避免隐私信息被记录,可以通过设置grails.exceptionresolver.params.exclude来屏蔽那些有关隐私字段的名称,比如:

grails.exceptionresolver.params.exclude = ['password', 'creditCard']

请求参数也可以通过设置grails.exceptionresolver.logRequestParametersfalse的方式来禁止掉。其运行于“开发”模式下,缺省值是true,除此之外的其他模式为false

grails.exceptionresolver.logRequestParameters=false

Logger inheritance

Earlier, we mentioned that all loggers inherit from the root logger and that loggers are hierarchical based on '.'-separated terms. What this means is that unless you override a parent setting, a logger retains the level and the appenders configured for that parent. So with this configuration:

log4j = {
    appenders {
        file name:'file', file:'/var/logs/mylog.log'
    }
    root {
        debug 'stdout', 'file'
    }
}

all loggers in the application will have a level of 'debug' and will log to both the 'stdout' and 'file' appenders. What if you only want to log to 'stdout' for a particular logger? Change the 'additivity' for a logger in that case.

Additivity simply determines whether a logger inherits the configuration from its parent. If additivity is false, then its not inherited. The default for all loggers is true, i.e. they inherit the configuration. So how do you change this setting? Here's an example:

log4j = {
    appenders {
        …
    }
    root {
        …
    }

info additivity: false stdout: ["grails.app.controllers.BookController", "grails.app.services.BookService"] }

So when you specify a log level, add an 'additivity' named argument. Note that you when you specify the additivity, you must configure the loggers for a named appender. The following syntax will not work:

info additivity: false, ["grails.app.controllers.BookController",
                         "grails.app.services.BookService"]

记录器的继承

早期,我们提到过所有的记录器都是从跟记录器继承而来的,其继承的层次是通过'.'来分割的。这意味着一个记录器将一直使用其上一级的级别和输出器配置,当然了你覆盖除外。以如下配置为例:

log4j = {
    appenders {
        file name:'file', file:'/var/logs/mylog.log'
    }
    root {
        debug 'stdout', 'file'
    }
}

此应用的所有记录器都是'debug'级别和'file'输出。拿如果我想给'stdout'使用特定类型的记录器,该如何做呢?修改记录器的'additivity'。

记录器使用'additivity'来检查是否要继承其父级配置,如果其值是false,就不再继承。缺省情况下,所有记录器都是true,即他们都继承自父级配置。那么我们该如何修改此配置呢?请看下例:

log4j = {
    appenders {
        …
    }
    root {
        …
    }

info additivity: false stdout: ["grails.app.controllers.BookController", "grails.app.services.BookService"] }

当你指定记录级别的时候,增加一个名为'additivity'的参数。需要注意的是,当使用此参数时,你必须要为其分配一个输出器。如下的配置将_不会_工作:

info additivity: false, ["grails.app.controllers.BookController",
                         "grails.app.services.BookService"]

Customizing stack trace printing and filtering

Stacktraces in general and those generated when using Groovy in particular are quite verbose and contain many stack frames that aren't interesting when diagnosing problems. So Grails uses a implementation of the org.codehaus.groovy.grails.exceptions.StackTraceFilterer interface to filter out irrelevant stack frames. To customize the approach used for filtering, implement that interface in a class in src/groovy or src/java and register it in Config.groovy:

grails.logging.stackTraceFiltererClass =
         'com.yourcompany.yourapp.MyStackTraceFilterer'

In addition, Grails customizes the display of the filtered stacktrace to make the information more readable. To customize this, implement the org.codehaus.groovy.grails.exceptions.StackTracePrinter interface in a class in src/groovy or src/java and register it in Config.groovy:

grails.logging.stackTracePrinterClass =
         'com.yourcompany.yourapp.MyStackTracePrinter'

Finally, to render error information in the error GSP, an HTML-generating printer implementation is needed. The default implementation is org.codehaus.groovy.grails.web.errors.ErrorsViewStackTracePrinter and it's registered as a Spring bean. To use your own implementation, either implement the org.codehaus.groovy.grails.exceptions.StackTraceFilterer directly or subclass ErrorsViewStackTracePrinter and register it in grails-app/conf/spring/resources.groovy as:

import com.yourcompany.yourapp.MyErrorsViewStackTracePrinter

beans = {

errorsViewStackTracePrinter(MyErrorsViewStackTracePrinter, ref('grailsResourceLocator')) }

自定义栈跟踪的输出和过滤

总的来说,Groovy生成的那些特定栈跟踪信息是比较冗余的,并且对于诊断问题也造成不少的干扰。因此Grails使用org.codehaus.groovy.grails.exceptions.StackTraceFilterer接口来完成对不相关信息的过滤。要完成对特定信息的过滤,只需要再src/groovy或者src/java中实现上述接口,并且在Config.groovy注册一下即可:

grails.logging.stackTraceFiltererClass =
         'com.yourcompany.yourapp.MyStackTraceFilterer'

此外,Grails也可以让这些被过滤的栈信息更具有可读性,你只需要在src/groovy或者src/java下边实现org.codehaus.groovy.grails.exceptions.StackTracePrinter接口,并且在Config.groovy中注册即可:

grails.logging.stackTracePrinterClass =
         'com.yourcompany.yourapp.MyStackTracePrinter'

最后,为了能够在错误GSP页面中渲染出错信息,需要一个生成HTML的打印输出,其缺省的实现是org.codehaus.groovy.grails.web.errors.ErrorsViewStackTracePrinter,并且被注册为一个Spring服务。你也可以通过实现org.codehaus.groovy.grails.exceptions.StackTraceFilterer接口或者定义ErrorsViewStackTracePrinter的子类来实现属于自己的渲染器,并且将其在grails-app/conf/spring/resources.groovy中注册,如下:

import com.yourcompany.yourapp.MyErrorsViewStackTracePrinter

beans = {

errorsViewStackTracePrinter(MyErrorsViewStackTracePrinter, ref('grailsResourceLocator')) }

Alternative logging libraries

By default, Grails uses Log4J to do its logging. For most people this is absolutely fine, and many users don't even care what logging library is used. But if you're not one of those and want to use an alternative, such as the JDK logging package or logback, you can do so by simply excluding a couple of dependencies from the global set and adding your own:

grails.project.dependency.resolution = {
    inherits("global") {
        excludes "grails-plugin-logging", "log4j"
    }
    …
    dependencies {
        runtime "ch.qos.logback:logback-core:0.9.29"
        …
    }
    …
}

If you do this, you will get unfiltered, standard Java stacktraces in your log files and you won't be able to use the logging configuration DSL that's just been described. Instead, you will have to use the standard configuration mechanism for the library you choose.

替换日志框架

缺省情况下,Grails使用Log4J来完成日志操作。对大多数的人来说,这绝对绰绰有余,而且很多的用户也根本就不关心使用那个日志框架。但是,如果你不是那些大多数,并且确实很想使用另外一个替代品,比如JDK自带的日志包或者logback。这时候,你只需要简单地全局设置中排除一些依赖,并且添加你自己地依赖即可,比如:

grails.project.dependency.resolution = {
    inherits("global") {
        excludes "grails-plugin-logging", "log4j"
    }
    …
    dependencies {
        runtime "ch.qos.logback:logback-core:0.9.29"
        …
    }
    …
}

如果你这么做了,那么你将在你的日志文件中记录未过滤的、标准的Java跟踪栈,此外你也不能使用先前介绍的DSL来进行日志配置,你只能使用你选择的日志框架所提供的配置机制。

3.1.3 配置GORM

Grails provides the following GORM configuration options:
  • grails.gorm.failOnError - If set to true, causes the save() method on domain classes to throw a grails.validation.ValidationException if validation fails during a save. This option may also be assigned a list of Strings representing package names. If the value is a list of Strings then the failOnError behavior will only be applied to domain classes in those packages (including sub-packages). See the save method docs for more information.

For example, to enable failOnError for all domain classes:

grails.gorm.failOnError=true

and to enable failOnError for domain classes by package:

grails.gorm.failOnError = ['com.companyname.somepackage',
                           'com.companyname.someotherpackage']
  • grails.gorm.autoFlush = If set to true, causes the merge, save and delete methods to flush the session, replacing the need to explicitly flush using save(flush: true).

Grails提供了如下的GORM配置选项:

  • grails.gorm.failOnError - 如果此选项值为 true 并且在保存的时候 校验 失败,那么此领域类的 save() 方法将抛出一个 grails.validation.ValidationException 异常。此选项的值还可以是代表包名的字符串列表。如果是字符串列表的话,那么failOnError仅仅作用于属于这些包名(包括子包名)的领域类。更多详细信息请参考 save 方法

例如要使所有的领域类都能够failOnError,配置如下:

grails.gorm.failOnError=true

要使特定包名的领域类能够failOnError,代码如下:

grails.gorm.failOnError = ['com.companyname.somepackage',
                           'com.companyname.someotherpackage']
  • grails.gorm.autoFlush = 如果此选项值为 true,那么将导致 mergesavedelete 方法不需要明确地指定flush参数(比如 save(flush: true) )而清除会话的行为。

3.2 环境

Per Environment Configuration

Grails supports the concept of per environment configuration. The Config.groovy, DataSource.groovy, and BootStrap.groovy files in the grails-app/conf directory can use per-environment configuration using the syntax provided by ConfigSlurper. As an example consider the following default DataSource definition provided by Grails:

dataSource {
    pooled = false
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
environments {
    development {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:h2:mem:devDb"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:prodDb"
        }
    }
}

Notice how the common configuration is provided at the top level and then an environments block specifies per environment settings for the dbCreate and url properties of the DataSource.

不同环境(Per Environment)配置

Grails支持不同环境配置的概念。grails-app/conf目录下的Config.groovyDataSource.groovyBootStrap.groovy文件都支持 ConfigSlurper 语法的不同环境配置。Grails自带的缺省DataSource定义,就是一个很好的示例:

dataSource {
    pooled = false
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
environments {
    development {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:h2:mem:devDb"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:prodDb"
        }
    }
}

请留意上述示例中顶层部分公共配置,以及不同环境下environments代码块中DataSourcedbCreateurl属性。

Packaging and Running for Different Environments

Grails' command line has built in capabilities to execute any command within the context of a specific environment. The format is:

grails [environment] [command name]

In addition, there are 3 preset environments known to Grails: dev, prod, and test for development, production and test. For example to create a WAR for the test environment you wound run:

grails test war

To target other environments you can pass a grails.env variable to any command:

grails -Dgrails.env=UAT run-app

不同环境下的运行和打包

Grails的命令行中内置了特定环境下执行命令的能力,其格式为:

grails [environment] [command name]

此外,Grails预置了三种开发环境:devprodtest,分别代表了开发生产测试环境。比如,要创建test环境的WAR,可以运行如下命令:

grails test war

要设置其他的环境,请使用grails.env变量:

grails -Dgrails.env=UAT run-app

Programmatic Environment Detection

Within your code, such as in a Gant script or a bootstrap class you can detect the environment using the Environment class:

import grails.util.Environment

...

switch (Environment.current) { case Environment.DEVELOPMENT: configureForDevelopment() break case Environment.PRODUCTION: configureForProduction() break }

可编程的环境检测

在你的代码中,比如Gant脚本或者启动类,你通过Environment类可以检测到当前的环境,比如:

import grails.util.Environment

...

switch (Environment.current) { case Environment.DEVELOPMENT: configureForDevelopment() break case Environment.PRODUCTION: configureForProduction() break }

Per Environment Bootstrapping

Its often desirable to run code when your application starts up on a per-environment basis. To do so you can use the grails-app/conf/BootStrap.groovy file's support for per-environment execution:

def init = { ServletContext ctx ->
    environments {
        production {
            ctx.setAttribute("env", "prod")
        }
        development {
            ctx.setAttribute("env", "dev")
        }
    }
    ctx.setAttribute("foo", "bar")
}

不同环境下的启动

通常你的应用启动时,需要根据不同的环境运行相应的代码,为此你可以使用grails-app/conf/BootStrap.groovy文件来执行不同环境下的处理:

def init = { ServletContext ctx ->
    environments {
        production {
            ctx.setAttribute("env", "prod")
        }
        development {
            ctx.setAttribute("env", "dev")
        }
    }
    ctx.setAttribute("foo", "bar")
}

Generic Per Environment Execution

The previous BootStrap example uses the grails.util.Environment class internally to execute. You can also use this class yourself to execute your own environment specific logic:

Environment.executeForCurrentEnvironment {
    production {
        // do something in production
    }
    development {
        // do something only in development
    }
}

不同环境下的通用处理

在以前的BootStrap示例中,我们使用grails.util.Environment类做了内部处理。你也可以通过此类来执行特定环境中的逻辑,比如:

Environment.executeForCurrentEnvironment {
    production {
        // do something in production
    }
    development {
        // do something only in development
    }
}

3.3 数据源

Since Grails is built on Java technology setting up a data source requires some knowledge of JDBC (the technology that doesn't stand for Java Database Connectivity).

If you use a database other than H2 you need a JDBC driver. For example for MySQL you would need Connector/J

Drivers typically come in the form of a JAR archive. It's best to use Ivy to resolve the jar if it's available in a Maven repository, for example you could add a dependency for the MySQL driver like this:

grails.project.dependency.resolution = {
    inherits("global")
    log "warn"
    repositories {
        grailsPlugins()
        grailsHome()
        grailsCentral()
        mavenCentral()
    }
    dependencies {
        runtime 'mysql:mysql-connector-java:5.1.16'
    }
}

Note that the built-in mavenCentral() repository is included here since that's a reliable location for this library.

If you can't use Ivy then just put the JAR in your project's lib directory.

Once you have the JAR resolved you need to get familiar Grails' DataSource descriptor file located at grails-app/conf/DataSource.groovy. This file contains the dataSource definition which includes the following settings:

  • driverClassName - The class name of the JDBC driver
  • username - The username used to establish a JDBC connection
  • password - The password used to establish a JDBC connection
  • url - The JDBC URL of the database
  • dbCreate - Whether to auto-generate the database from the domain model - one of 'create-drop', 'create', 'update' or 'validate'
  • pooled - Whether to use a pool of connections (defaults to true)
  • logSql - Enable SQL logging to stdout
  • formatSql - Format logged SQL
  • dialect - A String or Class that represents the Hibernate dialect used to communicate with the database. See the org.hibernate.dialect package for available dialects.
  • readOnly - If true makes the DataSource read-only, which results in the connection pool calling setReadOnly(true) on each Connection
  • properties - Extra properties to set on the DataSource bean. See the Commons DBCP BasicDataSource documentation.

A typical configuration for MySQL may be something like:

dataSource {
    pooled = true
    dbCreate = "update"
    url = "jdbc:mysql://localhost/yourDB"
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    username = "yourUser"
    password = "yourPassword"
}

When configuring the DataSource do not include the type or the def keyword before any of the configuration settings as Groovy will treat these as local variable definitions and they will not be processed. For example the following is invalid:

dataSource {
    boolean pooled = true // type declaration results in ignored local variable
    …
}

Example of advanced configuration using extra properties:

dataSource {
    pooled = true
    dbCreate = "update"
    url = "jdbc:mysql://localhost/yourDB"
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    username = "yourUser"
    password = "yourPassword"
    properties {
        maxActive = 50
        maxIdle = 25
        minIdle = 5
        initialSize = 5
        minEvictableIdleTimeMillis = 60000
        timeBetweenEvictionRunsMillis = 60000
        maxWait = 10000
        validationQuery = "/* ping */"
    }
}

既然Grails是基于Java技术来设置数据源的,那么一些JDBC的知识是必不可少的。

如果你的数据库并非H2,那么你至少还需要一个JDBC驱动。以MySQL为例,你需要下载Connector/J

驱动通常打包成JAR。如果你需要的jar在Maven的存储库中存在,那么最好是通过Ivy来解析它们,比如下面是对MySQL驱动的依赖:

grails.project.dependency.resolution = {
    inherits("global")
    log "warn"
    repositories {
        grailsPlugins()
        grailsHome()
        grailsCentral()
        mavenCentral()
    }
    dependencies {
        runtime 'mysql:mysql-connector-java:5.1.16'
    }
}

注意,此处引入了内置的mavenCentral()存储库,因为此jar包位于其中。

如果Ivy找不到,那么只需要将JAR放到你工程的lib目录即可。

一旦解决了JAR的问题,你需要来熟悉一下位于grails-app/conf/DataSource.groovy中的Grails数据源描述了。此文件包含如下所述的一些数据源的定义:

  • driverClassName - JDBC驱动的类名
  • username - 建立JDBC连接的用户名
  • password - 建立JDBC连接的密码
  • url - JDBC数据库的URL
  • dbCreate - 是否根据领域类自动生成数据库-可以是'create-drop'、'create'、'update'或者'validate'
  • pooled - 是否使用连接池(缺省是true)
  • logSql - 是否将SQL输出到字符终端
  • formatSql - 格式化SQL
  • dialect - Hibernate用于跟数据库通讯的方言(dialect),可以是字符串或者类名。可以通过org.hibernate.dialect来查看所支撑的方言。
  • readOnly - 如果是true那么此数据源就是只读的,这是通过调用连接池的ConnectionsetReadOnly(true)来实现的。
  • properties - 设置数据源的额外属性。更多请参考Commons DBCP的 BasicDataSource文档。

一个MySQL的典型配置可能如下:

dataSource {
    pooled = true
    dbCreate = "update"
    url = "jdbc:mysql://localhost/yourDB"
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    username = "yourUser"
    password = "yourPassword"
}

在配置数据源的时候,不要在配置名前加入任何类型或者def关键字,因为Groovy将其视为一个本地变量定义而将其忽略。比如:

dataSource {
    boolean pooled = true // type declaration results in ignored local variable
    …
}

一个使用额外属性配置的高级示例如下:

dataSource {
    pooled = true
    dbCreate = "update"
    url = "jdbc:mysql://localhost/yourDB"
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    username = "yourUser"
    password = "yourPassword"
    properties {
        maxActive = 50
        maxIdle = 25
        minIdle = 5
        initialSize = 5
        minEvictableIdleTimeMillis = 60000
        timeBetweenEvictionRunsMillis = 60000
        maxWait = 10000
        validationQuery = "/* ping */"
    }
}

More on dbCreate

Hibernate can automatically create the database tables required for your domain model. You have some control over when and how it does this through the dbCreate property, which can take these values:

  • create - Drops the existing schemaCreates the schema on startup, dropping existing tables, indexes, etc. first.
  • create-drop - Same as create, but also drops the tables when the application shuts down cleanly.
  • update - Creates missing tables and indexes, and updates the current schema without dropping any tables or data. Note that this can't properly handle many schema changes like column renames (you're left with the old column containing the existing data).
  • validate - Makes no changes to your database. Compares the configuration with the existing database schema and reports warnings.
  • any other value - does nothing

You can also remove the dbCreate setting completely, which is recommended once your schema is relatively stable and definitely when your application and database are deployed in production. Database changes are then managed through proper migrations, either with SQL scripts or a migration tool like Liquibase (the Database Migration plugin uses Liquibase and is tightly integrated with Grails and GORM).

关于dbCreate

Hibernate能够根据你的领域类来自动创建数据库表。你可以通过dbCreate属性来进行一些控制,其可选值如下:

  • create - 在启动时候,先删除已存在的,包括表、索引等,然后创建。
  • create-drop - 同 create,不过在应用关闭的时候,也进行表删除。
  • update - 创建不存在的表和索引,并且在不删除表和数据的情况下更新表结构。注意此种情况于很多限制,比如你不能很好地处理重命名字段(旧有地字段依然保留)
  • validate - 不改变你数据库地任何信息,只是跟现有地数据库配置脚本进行比较,并且报告一个警告。
  • 其他 - 什么都不做

如果你的数据库变化相对稳定或者你的应用部署于生产环境,推荐你将dbCreate完全移除。数据库的变更迁移可以通过SQL脚本或者迁移工具,比如Liquibase数据库迁移 插件就是通过Liquibase来跟Grails和GORM紧密集成的)来完成。

3.3.1 数据源和环境

The previous example configuration assumes you want the same config for all environments: production, test, development etc.

Grails' DataSource definition is "environment aware", however, so you can do:

dataSource {
    pooled = true
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    // other common settings here
}

environments { production { dataSource { url = "jdbc:mysql://liveip.com/liveDb" // other environment-specific settings here } } }

在前面的配置示例中,不管是在生产、测试还是开发环境中,我们假设所有的配置都是一样的,

但是Grails的数据源定义是可以跟环境相关的,因此,你可以象下面的示例那样进行处理:

dataSource {
    pooled = true
    driverClassName = "com.mysql.jdbc.Driver"
    dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    // other common settings here
}

environments { production { dataSource { url = "jdbc:mysql://liveip.com/liveDb" // other environment-specific settings here } } }

3.3.2 JNDI数据源

Referring to a JNDI DataSource

Most Java EE containers supply DataSource instances via Java Naming and Directory Interface (JNDI). Grails supports the definition of JNDI data sources as follows:

dataSource {
    jndiName = "java:comp/env/myDataSource"
}

The format on the JNDI name may vary from container to container, but the way you define the DataSource in Grails remains the same.

引用JNDI数据源

大部分的Java EE容器支持 Java命名服务接口 (JNDI)的DataSource,Grails也支持如下格式的JNDI数据源定义:

dataSource {
    jndiName = "java:comp/env/myDataSource"
}

虽然不同的容器之间定义JNDI名字的格式有很大的差异,但是Grails中DataSource的定义却是保持一致的。

Configuring a Development time JNDI resource

The way in which you configure JNDI data sources at development time is plugin dependent. Using the Tomcat plugin you can define JNDI resources using the grails.naming.entries setting in grails-app/conf/Config.groovy:

grails.naming.entries = [
    "bean/MyBeanFactory": [
        auth: "Container",
        type: "com.mycompany.MyBean",
        factory: "org.apache.naming.factory.BeanFactory",
        bar: "23"
    ],
    "jdbc/EmployeeDB": [
        type: "javax.sql.DataSource", //required
        auth: "Container", // optional
        description: "Data source for Foo", //optional
        driverClassName: "org.h2.Driver",
        url: "jdbc:h2:mem:database",
        username: "dbusername",
        password: "dbpassword",
        maxActive: "8",
        maxIdle: "4"
    ],
    "mail/session": [
        type: "javax.mail.Session,
        auth: "Container",
        "mail.smtp.host": "localhost"
    ]
]

开发环境中配置JNDI资源

在开发环境中,配置JNDI数据源的方式是跟插件相关的。如果你是在用 Tomcat 插件的话,可以通过grails-app/conf/Config.groovy中的grails.naming.entries来配置JNDI资源,比如:

grails.naming.entries = [
    "bean/MyBeanFactory": [
        auth: "Container",
        type: "com.mycompany.MyBean",
        factory: "org.apache.naming.factory.BeanFactory",
        bar: "23"
    ],
    "jdbc/EmployeeDB": [
        type: "javax.sql.DataSource", //required
        auth: "Container", // optional
        description: "Data source for Foo", //optional
        driverClassName: "org.h2.Driver",
        url: "jdbc:h2:mem:database",
        username: "dbusername",
        password: "dbpassword",
        maxActive: "8",
        maxIdle: "4"
    ],
    "mail/session": [
        type: "javax.mail.Session,
        auth: "Container",
        "mail.smtp.host": "localhost"
    ]
]

3.3.3 自动数据库迁移

The dbCreate property of the DataSource definition is important as it dictates what Grails should do at runtime with regards to automatically generating the database tables from GORM classes. The options are described in the DataSource section:
  • create
  • create-drop
  • update
  • validate
  • no value

In development mode dbCreate is by default set to "create-drop", but at some point in development (and certainly once you go to production) you'll need to stop dropping and re-creating the database every time you start up your server.

It's tempting to switch to update so you retain existing data and only update the schema when your code changes, but Hibernate's update support is very conservative. It won't make any changes that could result in data loss, and doesn't detect renamed columns or tables, so you'll be left with the old one and will also have the new one.

Grails supports Rails-style migrations via the Database Migration plugin which can be installed by running


grails install-plugin database-migration

The plugin uses Liquibase and and provides access to all of its functionality, and also has support for GORM (for example generating a change set by comparing your domain classes to a database).

DataSource中的dbCreate属性是很重要的,正如其所暗示,Grails将根据此值和GORM类在运行时来自动生成数据库表。可选项已经在数据源章节中介绍:

  • create
  • create-drop
  • update
  • validate

开发模式下,dbCreate通常设置为"create-drop",但是当开发到一定程度(更进一步要运行于生产环境),你将不会再采用这种每次启动先删除再创建的方式。

当你想保留数据并且只想更新所变化的代码时,可以尝试使用update。不过Hibernate对更新的支撑是非常保守的。其在数据安全方面不能给你任何保证,此外它也不能自动检测到字段或者表的重命名,因此在新增的同时旧有的依然保留。

Grails现在支撑Rails风格的数据库迁移了,这是通过安装和运行 Database Migration 插件来实现的。


grails install-plugin database-migration

此插件以 Liquibase 为基础,除了具有原来的强大功能外,其还对GORM提供了支持(比如通过领域类和数据库的比较来自动生成变化内容)。

3.3.4 事务感知的数据源代理

The actual dataSource bean is wrapped in a transaction-aware proxy so you will be given the connection that's being used by the current transaction or Hibernate Session if one is active.

If this were not the case, then retrieving a connection from the dataSource would be a new connection, and you wouldn't be able to see changes that haven't been committed yet (assuming you have a sensible transaction isolation setting, e.g. READ_COMMITTED or better).

The "real" unproxied dataSource is still available to you if you need access to it; its bean name is dataSourceUnproxied.

You can access this bean like any other Spring bean, i.e. using dependency injection:

class MyService {

def dataSourceUnproxied … }

or by pulling it from the ApplicationContext:

def dataSourceUnproxied = ctx.dataSourceUnproxied

dataSource 实际上只是事务感知代理的封装,因此你得到的数据库连接是来自于当前事务或者当前活动的Hibernate的Session

除此之外,直接从dataSource获取的连接将是一个新连接,你将看不到任何没有提交的变化(假设你设置了事务隔离敏感度,比如READ_COMMITTED或者更高)。

那个"真实的"未被代理的dataSource对你来说,依然可用,只不过其bean名称是dataSourceUnproxied

你可用想其他Spring bean那样来访问此bean,比如使用依赖注入:

class MyService {

def dataSourceUnproxied … }

或者直接从ApplicationContext中获取:

def dataSourceUnproxied = ctx.dataSourceUnproxied

3.3.5 数据库管理界面

The H2 database console is a convenient feature of H2 that provides a web-based interface to any database that you have a JDBC driver for, and it's very useful to view the database you're developing against. It's especially useful when running against an in-memory database.

You can access the console by navigating to http://localhost:8080/appname/dbconsole in a browser. The URI can be configured using the grails.dbconsole.urlRoot attribute in Config.groovy and defaults to '/dbconsole'.

The console is enabled by default in development mode and can be disabled or enabled in other environments by using the grails.dbconsole.enabled attribute in Config.groovy. For example you could enable the console in production using

environments {
    production {
        grails.serverURL = "http://www.changeme.com"
        grails.dbconsole.enabled = true
        grails.dbconsole.urlRoot = '/admin/dbconsole'
    }
    development {
        grails.serverURL = "http://localhost:8080/${appName}"
    }
    test {
        grails.serverURL = "http://localhost:8080/${appName}"
    }
}

If you enable the console in production be sure to guard access to it using a trusted security framework.

H2数据库管理界面是在H2特性的基础上提供的一个基于WEB界面的数据库管理,用以管理任何基于JDBC的数据库,在开发阶段用来查看数据库非常有用,尤其在你的应用运行于数据库的内存模式时。

你可以在浏览器中通过 http://localhost:8080/appname/dbconsole 来使用访问。此URI可以通过配置Config.groovy中的grails.dbconsole.urlRoot属性来改变,缺省是'/dbconsole'

此界面在开发模式下缺省是有效的,你也可以通过修改Config.groovy中的grails.dbconsole.enabled属性来使其在其他环境模式下失效或者生效。比如,你可以在生产环境中使其生效:

environments {
    production {
        grails.serverURL = "http://www.changeme.com"
        grails.dbconsole.enabled = true
        grails.dbconsole.urlRoot = '/admin/dbconsole'
    }
    development {
        grails.serverURL = "http://localhost:8080/${appName}"
    }
    test {
        grails.serverURL = "http://localhost:8080/${appName}"
    }
}

如果你要在生产环境中使用此功能,请确保使用安全可信的框架来保护。

Configuration

By default the console is configured for an H2 database which will work with the default settings if you haven't configured an external database - you just need to change the JDBC URL to jdbc:h2:mem:devDB. If you've configured an external database (e.g. MySQL, Oracle, etc.) then you can use the Saved Settings dropdown to choose a settings template and fill in the url and username/password information from your DataSource.groovy.

配置

缺省情况下,如果你没有使用外部数据库,那么数据库管理界面是使用H2数据库的,其JDBC的URL配置成jdbc:h2:mem:devDB即可。但是如果你使用的是一个外部数据库(比如MySQL、 Oracle等),那你需要从下拉框中选择合适的JDBC配置模板,并且配置合适的url、用户名/密码,要确保跟DataSource.groovy的配置是一致的。

3.3.6 多数据源

By default all domain classes share a single DataSource and a single database, but you have the option to partition your domain classes into two or more DataSources.

缺省情况下,所有的领域类共享同一个DataSource和数据库,但是你还是有将领域类拆分到两个甚至更多个DataSource的选择的。

Configuring Additional DataSources

The default DataSource configuration in grails-app/conf/DataSource.groovy looks something like this:

dataSource {
    pooled = true
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}

environments { development { dataSource { dbCreate = "create-drop" url = "jdbc:h2:mem:devDb" } } test { dataSource { dbCreate = "update" url = "jdbc:h2:mem:testDb" } } production { dataSource { dbCreate = "update" url = "jdbc:h2:prodDb" } } }

This configures a single DataSource with the Spring bean named dataSource. To configure extra DataSources, add another dataSource block (at the top level, in an environment block, or both, just like the standard DataSource definition) with a custom name, separated by an underscore. For example, this configuration adds a second DataSource, using MySQL in the development environment and Oracle in production:

environments {
    development {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:h2:mem:devDb"
        }
        dataSource_lookup {
            dialect = org.hibernate.dialect.MySQLInnoDBDialect
            driverClassName = 'com.mysql.jdbc.Driver'
            username = 'lookup'
            password = 'secret'
            url = 'jdbc:mysql://localhost/lookup'
            dbCreate = 'update'
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:prodDb"
        }
        dataSource_lookup {
            dialect = org.hibernate.dialect.Oracle10gDialect
            driverClassName = 'oracle.jdbc.driver.OracleDriver'
            username = 'lookup'
            password = 'secret'
            url = 'jdbc:oracle:thin:@localhost:1521:lookup'
            dbCreate = 'update'
        }
    }
}

You can use the same or different databases as long as they're supported by Hibernate.

配置额外的数据源

缺省的DataSource配置是位于grails-app/conf/DataSource.groovy中的,大体样子如下:

dataSource {
    pooled = true
    driverClassName = "org.h2.Driver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}

environments { development { dataSource { dbCreate = "create-drop" url = "jdbc:h2:mem:devDb" } } test { dataSource { dbCreate = "update" url = "jdbc:h2:mem:testDb" } } production { dataSource { dbCreate = "update" url = "jdbc:h2:prodDb" } } }

上述示例配置了一个Spring bean名称为dataSourceDataSource。要配置额外的DataSource,需要增加另外一个自定义名称(以下划线分割)的dataSource(跟标准的DataSource类似,只不过要定义在在最外层、环境代码块或者同时两个地方)代码块即可。例如,以下代码的配置新增了第二个DataSource,其在开发环境下是MySQL数据库,在生产环境下是Oracle:

environments {
    development {
        dataSource {
            dbCreate = "create-drop"
            url = "jdbc:h2:mem:devDb"
        }
        dataSource_lookup {
            dialect = org.hibernate.dialect.MySQLInnoDBDialect
            driverClassName = 'com.mysql.jdbc.Driver'
            username = 'lookup'
            password = 'secret'
            url = 'jdbc:mysql://localhost/lookup'
            dbCreate = 'update'
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:h2:prodDb"
        }
        dataSource_lookup {
            dialect = org.hibernate.dialect.Oracle10gDialect
            driverClassName = 'oracle.jdbc.driver.OracleDriver'
            username = 'lookup'
            password = 'secret'
            url = 'jdbc:oracle:thin:@localhost:1521:lookup'
            dbCreate = 'update'
        }
    }
}

你可以使用Hibernate所支持的相同和或者相异的数据库。

Configuring Domain Classes

If a domain class has no DataSource configuration, it defaults to the standard 'dataSource'. Set the datasource property in the mapping block to configure a non-default DataSource. For example, if you want to use the ZipCode domain to use the 'lookup' DataSource, configure it like this;

class ZipCode {

String code

static mapping = { datasource 'lookup' } }

A domain class can also use two or more DataSources. Use the datasources property with a list of names to configure more than one, for example:

class ZipCode {

String code

static mapping = { datasources(['lookup', 'auditing']) } }

If a domain class uses the default DataSource and one or more others, use the special name 'DEFAULT' to indicate the default DataSource:

class ZipCode {

String code

static mapping = { datasources(['lookup', 'DEFAULT']) } }

If a domain class uses all configured DataSources use the special value 'ALL':

class ZipCode {

String code

static mapping = { datasource 'ALL' } }

配置领域类

如果一个领域类没有配置DataSource,那么其缺省使用标准的'dataSource'。你可以在mapping代码块中设置datasource属性来配置一个非标准的DataSource。例如,你希望ZipCode使用名为'lookup'DataSource,其配置如下:

class ZipCode {

String code

static mapping = { datasource 'lookup' } }

一个领域类还可以有两个甚至更多个DataSources。这时候,只需要将datasources设置为一个名称的列表即可,比如:

class ZipCode {

String code

static mapping = { datasources(['lookup', 'auditing']) } }

如果一个领域类既使用缺省的又使用多于一个的DataSource,可以使用名称为'DEFAULT'来代表缺省的DataSource

class ZipCode {

String code

static mapping = { datasources(['lookup', 'DEFAULT']) } }

如果一个领域类要使用所有已经配置的DataSource,请使用特定名称'ALL'

class ZipCode {

String code

static mapping = { datasource 'ALL' } }

Namespaces and GORM Methods

If a domain class uses more than one DataSource then you can use the namespace implied by each DataSource name to make GORM calls for a particular DataSource. For example, consider this class which uses two DataSources:

class ZipCode {

String code

static mapping = { datasources(['lookup', 'auditing']) } }

The first DataSource specified is the default when not using an explicit namespace, so in this case we default to 'lookup'. But you can call GORM methods on the 'auditing' DataSource with the DataSource name, for example:

def zipCode = ZipCode.auditing.get(42)
…
zipCode.auditing.save()

As you can see, you add the DataSource to the method call in both the static case and the instance case.

命名空间和GORM方法

如果一个领域类使用了多于一个的DataSource,你可以将其每个DataSource名称作为命名空间,并且以此命名来执行GORM的方法调用。例如,下面示例的类有两个DataSource

class ZipCode {

String code

static mapping = { datasources(['lookup', 'auditing']) } }

当没有明确指定命名空间的时候,其指定的第一个DataSource被视为缺省,上例中其缺省命名空间是'lookup'。但是你也可以在'auditing'的DataSource上执行GORM方法,比如:

def zipCode = ZipCode.auditing.get(42)
…
zipCode.auditing.save()

如你所见,你可以在DataSource上进行静态和实例类型的方法调用。

Services

Like Domain classes, by default Services use the default DataSource and PlatformTransactionManager. To configure a Service to use a different DataSource, use the static datasource property, for example:

class DataService {

static datasource = 'lookup'

void someMethod(...) { … } }

A transactional service can only use a single DataSource, so be sure to only make changes for domain classes whose DataSource is the same as the Service.

Note that the datasource specified in a service has no bearing on which datasources are used for domain classes; that's determined by their declared datasources in the domain classes themselves. It's used to declare which transaction manager to use.

What you'll see is that if you have a Foo domain class in dataSource1 and a Bar domain class in dataSource2, and WahooService uses dataSource1, a service method that saves a new Foo and a new Bar will only be transactional for Foo since they share the datasource. The transaction won't affect the Bar instance. If you want both to be transactional you'd need to use two services and XA datasources for two-phase commit, e.g. with the Atomikos plugin.

服务类

跟领域类相似,服务类也是使用缺省的DataSourcePlatformTransactionManager。要配置服务使用另外一个不同的DataSource,请使用静态的(static)datasource属性,比如:

class DataService {

static datasource = 'lookup'

void someMethod(...) { … } }

一个支持事务的服务只能使用一个DataSource,因此请确保领域类的DataSource的名字要跟服务类中定义的一致。

注意,一个服务类的datasource不对领域类的datasources产生影响,后者由其自身的声明决定。服务类的datasource多用来声明要使用哪一个事务管理器。

假设你有两个数据源,领域类Foo属于dataSource1,Bar属于dataSource2,而WahooService使用dataSource1,此外还有一个方法来实现Foo和Bar的新增保存,那么只有Foo是支持事务的,因为他们共享dataSource1数据源。而Bar实例并不受事务影响。如果你想两者都支持事务,那么你需要两个服务类和支持两阶段提交的XA数据源,比如使用Atomikos插件。

XA and Two-phase Commit

Grails has no native support for XA DataSources or two-phase commit, but the Atomikos plugin makes it easy. See the plugin documentation for the simple changes needed in your DataSource definitions to reconfigure them as XA DataSources.

XA和两阶段(Two-phase)提交

Grails并没有直接支持XA DataSources 或者 两阶段提交,但是Atomikos 插件使两者变得容易。此插件的文档有介绍如何比较容易的将现有的DataSource定义重新配置为XA DataSources。

3.4 外部配置

Some deployments require that configuration be sourced from more than one place and be changeable without requiring a rebuild of the application. In order to support deployment scenarios such as these the configuration can be externalized. To do so, point Grails at the locations of the configuration files that should be used by adding a grails.config.locations setting in Config.groovy, for example:

grails.config.locations = [
    "classpath:${appName}-config.properties",
    "classpath:${appName}-config.groovy",
    "file:${userHome}/.grails/${appName}-config.properties",
    "file:${userHome}/.grails/${appName}-config.groovy" ]

In the above example we're loading configuration files (both Java Properties files and ConfigSlurper configurations) from different places on the classpath and files located in USER_HOME.

It is also possible to load config by specifying a class that is a config script.

grails.config.locations = [com.my.app.MyConfig]

This can be useful in situations where the config is either coming from a plugin or some other part of your application. A typical use for this is re-using configuration provided by plugins across multiple applications.

Ultimately all configuration files get merged into the config property of the GrailsApplication object and are hence obtainable from there.

Values that have the same name as previously defined values will overwrite the existing values, and the pointed to configuration sources are loaded in the order in which they are defined.

某些部署要求配置信息可以放在多个源文件中,并且在不需要重现编译打包的情况下可被修改。为了能够支撑这些特定的部署场景,Grails的配置信息可以在外部进行配置。要完成此功能,只需要设置Config.groovy中的grails.config.locations的值来指向配置文件的位置即可,比如:

grails.config.locations = [
    "classpath:${appName}-config.properties",
    "classpath:${appName}-config.groovy",
    "file:${userHome}/.grails/${appName}-config.properties",
    "file:${userHome}/.grails/${appName}-config.groovy" ]

在上述示例中,我们从类路径(classpath)和USER_HOME下加载不同的配置文件(包括Java属性文件和Groovy的ConfigSlurper

也可以通过配置脚本的类名来加载,比如:

grails.config.locations = [com.my.app.MyConfig]

此种情况特别适合于你从插件或者应用的其他部分来加载配置,其典型的应用就是一个插件所提供的配置可以在多个应用中复用。

最后,所有的这些配置文件的内容将合并于GrailsApplicationconfig属性中,因此,要获取使用也是通过它来实现的。

配置中如果一个名字有多个值,那么新值将覆盖旧的,其顺序是根据源文件的加载顺序来定义的。

Config Defaults

The configuration values contained in the locations described by the grails.config.locations property will override any values defined in your application Config.groovy file which may not be what you want. You may want to have a set of default values be be loaded that can be overridden in either your application's Config.groovy file or in a named config location. For this you can use the grails.config.defaults.locations property.

This property supports the same values as the grails.config.locations property (i.e. paths to config scripts, property files or classes), but the config described by grails.config.defaults.locations will be loaded before all other values and can therefore be overridden. Some plugins use this mechanism to supply one or more sets of default configuration that you can choose to include in your application config.

Grails also supports the concept of property place holders and property override configurers as defined in Spring For more information on these see the section on Grails and Spring

缺省配置

通过grails.config.locations定义的配置将 优先于 你在Config.groovy文件中的任意值,这也许并非你所需要的。你希望Config.groovy或者特定位置的文件才能加载并且重载特定的 缺省值 ,这种情况,你可以通过配置grails.config.defaults.locations属性来实现。

此属性的值跟grails.config.locations一样(可以是特定路径下的配置脚本、属性文件或者类名),但与之不同的是通过grails.config.defaults.locations来配置的值比其他方式加载的 更早 ,因此也就可有被重载。有些插件就是通过此机制来实现一个或者多个缺省配置的。

Grails还支撑Spring 配置中属性占位符和覆盖的概念。更多信息请参考Grails和Spring章节

3.5 版本

Versioning Basics

Grails has built in support for application versioning. The version of the application is set to 0.1 when you first create an application with the create-app command. The version is stored in the application meta data file application.properties in the root of the project.

To change the version of your application you can edit the file manually, or run the set-version command:

grails set-version 0.2

The version is used in various commands including the war command which will append the application version to the end of the created WAR file.

版本基础

Grails内置了对应用版本的支持。当你第一次通过create-app来创建应用的时候,其版本设置为0.1。版本信息被存储在工程根目录下的application.properties文件种。

要改变应用的版本,你可以手工修改此文件,或者运行set-version命令,比如:

grails set-version 0.2

版本信息被使用在在不同的命令中,比如war命令就会在创建的WAR文件中追加应用的版本信息。

Detecting Versions at Runtime

You can detect the application version using Grails' support for application metadata using the GrailsApplication class. For example within controllers there is an implicit grailsApplication variable that can be used:

def version = grailsApplication.metadata['app.version']

You can retrieve the the version of Grails that is running with:

def grailsVersion = grailsApplication.metadata['app.grails.version']

or the GrailsUtil class:

import grails.util.GrailsUtil
…
def grailsVersion = GrailsUtil.grailsVersion

运行期间检测版本

你可以使用GrailsApplication类来检测应用的版本信息,比如在控制器中,就有一个隐含的grailsApplication变量可用:

def version = grailsApplication.metadata['app.version']

你还可用获取到Grails的版本信息:

def grailsVersion = grailsApplication.metadata['app.grails.version']

或者通过GrailsUtil类来获取Grails的版本:

import grails.util.GrailsUtil
…
def grailsVersion = GrailsUtil.grailsVersion

3.6 文档引擎

Since Grails 1.2, the documentation engine that powers the creation of this documentation has been available for your own Grails projects.

The documentation engine uses a variation on the Textile syntax to automatically create project documentation with smart linking, formatting etc.

从Grails 1.2以来,本文的文档就是通过文档引擎来创建的,并且对你的Grails项目文档也是有效的。

文档引擎在Textile语法基础上,进行了一些改动,以适应自动创建工程文档的需要,此文档支持灵活链接、格式化等功能。

Creating project documentation

To use the engine you need to follow a few conventions. First, you need to create a src/docs/guide directory where your documentation source files will go. Then, you need to create the source docs themselves. Each chapter should have its own gdoc file as should all numbered sub-sections. You will end up with something like:

+ src/docs/guide/introduction.gdoc
+ src/docs/guide/introduction/changes.gdoc
+ src/docs/guide/gettingStarted.gdoc
+ src/docs/guide/configuration.gdoc
+ src/docs/guide/configuration/build.gdoc
+ src/docs/guide/configuration/build/controllers.gdoc

Note that you can have all your gdoc files in the top-level directory if you want, but you can also put sub-sections in sub-directories named after the parent section - as the above example shows.

Once you have your source files, you still need to tell the documentation engine what the structure of your user guide is going to be. To do that, you add a src/docs/guide/toc.yml file that contains the structure and titles for each section. This file is in YAML format and basically represents the structure of the user guide in tree form. For example, the above files could be represented as:

introduction:
  title: Introduction
  changes: Change Log
gettingStarted: Getting Started
configuration:
  title: Configuration
  build:
    title: Build Config
    controllers: Specifying Controllers

The format is pretty straightforward. Any section that has sub-sections is represented with the corresponding filename (minus the .gdoc extension) followed by a colon. The next line should contain title: plus the title of the section as seen by the end user. Every sub-section then has its own line after the title. Leaf nodes, i.e. those without any sub-sections, declare their title on the same line as the section name but after the colon.

That's it. You can easily add, remove, and move sections within the toc.yml to restructure the generated user guide. You should also make sure that all section names, i.e. the gdoc filenames, should be unique since they are used for creating internal links and for the HTML filenames. Don't worry though, the documentation engine will warn you of duplicate section names.

创建工程文档

为了使用此引擎,你需要遵循一些约定。首先,你需要创建src/docs/guide目录用来存放文档的源文件。其次,需要创建文档文件,每一章节应该是独立的一个gdoc文件,并且还应该按照子章节的序号排列。比如下面示例:

+ src/docs/guide/introduction.gdoc
+ src/docs/guide/introduction/changes.gdoc
+ src/docs/guide/gettingStarted.gdoc
+ src/docs/guide/configuration.gdoc
+ src/docs/guide/configuration/build.gdoc
+ src/docs/guide/configuration/build/controllers.gdoc

注意,如果你喜欢,你可以将所有的gdoc文件都放在顶层的目录下。但是你也可以将子章节放到相应名称的子目录中,正如上例所示。

一旦你的源文件已经完成,你还是需要让文档引擎了知你文档的结构。为此你需要增加一个src/docs/guide/toc.yml文件,用以描述章节的结构和标题。 此YAML格式的文件用以表述手册的树形格式的结构。比如上述的文件可以用如下的描述:

introduction:
  title: Introduction
  changes: Change Log
gettingStarted: Getting Started
configuration:
  title: Configuration
  build:
    title: Build Config
    controllers: Specifying Controllers

此种格式是相当简洁易懂的,一个章节如果含有子章节的话,要在其对应的文件名(当然是去掉.gdoc后缀)后边添加一个冒号(:),紧随其后的一行必须是title:加对此章节的标题描述,在标题后边是每一个子章节的信息。如果子章节是一个叶子节点(不包含子章节的章节)其标题跟章节名称在同一行就好了,但要以冒号分割(可以参考上述示例--译者注)。

搞定!你可以轻松地添加、删除和移动toc.yml中的章节,这样就可以重新排版用户手册了。此外你也需要确认所有的章节名称(gdoc文件名称)是全局唯一的,因为那些内部超链接和HTML名称也要用到它们。不过也无需太担心,文档引擎将提示你那些重复的章节名称。

Creating reference items

Reference items appear in the Quick Reference section of the documentation. Each reference item belongs to a category and a category is a directory located in the src/docs/ref directory. For example, suppose you have defined a new controller method called renderPDF. That belongs to the Controllers category so you would create a gdoc text file at the following location:

+ src/docs/ref/Controllers/renderPDF.gdoc

创建条目(Item)引用

条目引用出现在文档的快速引用章节。每一个引用都属于一个类别,此类别位于src/docs/ref中。举例来说,假设你定义了一个新的控制器方法renderPDF,此方法属于Controllers类别,那么你应该在如下所示的位置创建一个gdoc文本文件:

+ src/docs/ref/Controllers/renderPDF.gdoc

Configuring Output Properties

There are various properties you can set within your grails-app/conf/Config.groovy file that customize the output of the documentation such as:

  • grails.doc.title - The title of the documentation
  • grails.doc.subtitle - The subtitle of the documentation
  • grails.doc.authors - The authors of the documentation
  • grails.doc.license - The license of the software
  • grails.doc.copyright - The copyright message to display
  • grails.doc.footer - The footer to use

Other properties such as the version are pulled from your project itself. If a title is not specified, the application name is used.

配置输出属性

grails-app/conf/Config.groovy文件中,你有很多不同的属性可以设置,用以自定义文档的输出,比如:

  • grails.doc.title - 文档的标题
  • grails.doc.subtitle - 文档的子标题
  • grails.doc.authors - 文档的作者
  • grails.doc.license - 软件的许可证
  • grails.doc.copyright - 要显示的版权信息
  • grails.doc.footer - 脚注

其他的一些,比如版本直接从你的项目中获取。如果标题没有设定,将缺省使用你的应用名称。

Generating Documentation

Once you have created some documentation (refer to the syntax guide in the next chapter) you can generate an HTML version of the documentation using the command:

grails doc

This command will output an docs/manual/index.html which can be opened in a browser to view your documentation.

生成文档

一旦你创建了文档(语法请参考下一节),你就可以生成HTML版本的文档了,命令如下:

grails doc

此命令将输出到docs/manual/index.html,这样你就可以在浏览器中查看文档了。

Documentation Syntax

As mentioned the syntax is largely similar to Textile or Confluence style wiki markup. The following sections walk you through the syntax basics.

文档语法

正如以前所述,文档语法跟Textile或者Confluence风格的wiki标签。下述章节将对基本的语法做个简单地介绍。

Basic Formatting

Monospace: monospace

@monospace@

Italic: italic

_italic_

Bold: bold

*bold*

Image:

!http://grails.org/images/new/grailslogo_topNav.png!

基本格式

等宽字体: monospace

@monospace@

斜体: italic

_italic_

黑体: bold

*bold*

图像:

!http://grails.org/images/new/grailslogo_topNav.png!

Linking

There are several ways to create links with the documentation generator. A basic external link can either be defined using confluence or textile style markup:

[SpringSource|http://www.springsource.com/]

or

"SpringSource":http://www.springsource.com/

For links to other sections inside the user guide you can use the guide: prefix with the name of the section you want to link to:

[Intro|guide:introduction]

The section name comes from the corresponding gdoc filename. The documentation engine will warn you if any links to sections in your guide break.

To link to reference items you can use a special syntax:

[controllers|renderPDF]

In this case the category of the reference item is on the left hand side of the | and the name of the reference item on the right.

Finally, to link to external APIs you can use the api: prefix. For example:

[String|api:java.lang.String]

The documentation engine will automatically create the appropriate javadoc link in this case. To add additional APIs to the engine you can configure them in grails-app/conf/Config.groovy. For example:

grails.doc.api.org.hibernate=
            "http://docs.jboss.org/hibernate/stable/core/javadocs"

The above example configures classes within the org.hibernate package to link to the Hibernate website's API docs.

超链接

文档生成链接的方式有几种,基本的外部链接(指向本文档之外的超链接--译者注)可以使用confluence或者textile风格的标签:

[SpringSource|http://www.springsource.com/]

或者

"SpringSource":http://www.springsource.com/

对于指向本文档其他章节的链接,可以使用guide:前缀和你要指向的章节名称:

[Intro|guide:introduction]

章节的名称要跟gdoc文件名称相对应。如果你指向的章节不存在,文档引擎会给你一个警告提示。

要指向条目引用,你可以使用如下特殊语法:

[controllers|renderPDF]

此种情况,要引用的类别名称在|左边,而要引用的条目名称位于右边。

最后,要指向外部API,你可以使用api:前缀。比如:

[String|api:java.lang.String]

文档引擎将自动生成合适的javadoc链接。对于额外的API,你可以通过grails-app/conf/Config.groovy来进行配置,比如:

grails.doc.api.org.hibernate=
            "http://docs.jboss.org/hibernate/stable/core/javadocs"

上述示例中,配置org.hibernate包指向Hibernate的官方API文档。

Lists and Headings

Headings can be created by specifying the letter 'h' followed by a number and then a dot:

h3.<space>Heading3
h4.<space>Heading4

Unordered lists are defined with the use of the * character:

* item 1
** subitem 1
** subitem 2
* item 2

Numbered lists can be defined with the # character:

# item 1

Tables can be created using the table macro:

NameNumber
Albert46
Wilma1348
James12

{table}
 *Name* | *Number*
 Albert | 46
 Wilma | 1348
 James | 12
{table}

列表和标头

标头可以通过字母'h'加数字再加一个点来表示,比如:

h3.<space>Heading3
h4.<space>Heading4

无序的列表可以通过*字符来定义:

* item 1
** subitem 1
** subitem 2
* item 2

有序的列表可以通过#来定义:

# item 1

表格可以通过宏table来实现:

NameNumber
Albert46
Wilma1348
James12

{table}
 *Name* | *Number*
 Albert | 46
 Wilma | 1348
 James | 12
{table}

Code and Notes

You can define code blocks with the code macro:

class Book {
    String title
}

{code}
class Book {
    String title
}
{code}

The example above provides syntax highlighting for Java and Groovy code, but you can also highlight XML markup:

<hello>world</hello>

{code:xml}
<hello>world</hello>
{code}

There are also a couple of macros for displaying notes and warnings:

Note:

This is a note!

{note}
This is a note!
{note}

Warning:

This is a warning!

{warning}
This is a warning!
{warning}

代码和提示

你可以通过宏code来定义代码块,比如:

class Book {
    String title
}

{code}
class Book {
    String title
}
{code}

上述示例展示了Java和Groovy代码的高亮语法显示,但是你也可以高亮XML标签,比如:

<hello>world</hello>

{code:xml}
<hello>world</hello>
{code}

还有一些用于提示和警告的宏:

Note:

This is a note!

{note}
This is a note!
{note}

Warning:

This is a warning!

{warning}
This is a warning!
{warning}

3.7 依赖解析

Grails features a dependency resolution DSL that lets you control how plugins and JAR dependencies are resolved.

You specify a grails.project.dependency.resolution property inside the grails-app/conf/BuildConfig.groovy file that configures how dependencies are resolved:

grails.project.dependency.resolution = {
   // config here
}

The default configuration looks like the following:

grails.project.class.dir = "target/classes"
grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
//grails.project.war.file = "target/${appName}-${appVersion}.war"

grails.project.dependency.resolution = { // inherit Grails' default dependencies inherits("global") { // uncomment to disable ehcache // excludes 'ehcache' } log "warn" repositories { grailsPlugins() grailsHome() grailsCentral()

// uncomment these to enable remote dependency resolution // from public Maven repositories //mavenCentral() //mavenLocal() //mavenRepo "http://snapshots.repository.codehaus.org" //mavenRepo "http://repository.codehaus.org" //mavenRepo "http://download.java.net/maven/2/" //mavenRepo "http://repository.jboss.com/maven2/" } dependencies { // specify dependencies here under either 'build', 'compile', // 'runtime', 'test' or 'provided' scopes eg.

// runtime 'mysql:mysql-connector-java:5.1.16' }

plugins { compile ":hibernate:$grailsVersion" compile ":jquery:1.6.1.1" compile ":resources:1.0"

build ":tomcat:$grailsVersion" } }

The details of the above will be explained in the next few sections.

Grails提供了依赖解析的DSL来处理插件和JAR的依赖处理。

你可以在grails-app/conf/BuildConfig.groovy中设置grails.project.dependency.resolution属性来配置依赖是如何解析的,模板如下:

grails.project.dependency.resolution = {
   // config here
}

其缺省的配置如下所示:

grails.project.class.dir = "target/classes"
grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
//grails.project.war.file = "target/${appName}-${appVersion}.war"

grails.project.dependency.resolution = { // inherit Grails' default dependencies inherits("global") { // uncomment to disable ehcache // excludes 'ehcache' } log "warn" repositories { grailsPlugins() grailsHome() grailsCentral()

// uncomment these to enable remote dependency resolution // from public Maven repositories //mavenCentral() //mavenLocal() //mavenRepo "http://snapshots.repository.codehaus.org" //mavenRepo "http://repository.codehaus.org" //mavenRepo "http://download.java.net/maven/2/" //mavenRepo "http://repository.jboss.com/maven2/" } dependencies { // specify dependencies here under either 'build', 'compile', // 'runtime', 'test' or 'provided' scopes eg.

// runtime 'mysql:mysql-connector-java:5.1.16' }

plugins { compile ":hibernate:$grailsVersion" compile ":jquery:1.6.1.1" compile ":resources:1.0"

build ":tomcat:$grailsVersion" } }

上述示例的详细描述将会在下来的几个章节中解释。

3.7.1 配置和依赖

Grails features five dependency resolution configurations (or 'scopes'):
  • build: Dependencies for the build system only
  • compile: Dependencies for the compile step
  • runtime: Dependencies needed at runtime but not for compilation (see above)
  • test: Dependencies needed for testing but not at runtime (see above)
  • provided: Dependencies needed at development time, but not during WAR deployment

Within the dependencies block you can specify a dependency that falls into one of these configurations by calling the equivalent method. For example if your application requires the MySQL driver to function at runtime you can specify that like this:

runtime 'com.mysql:mysql-connector-java:5.1.16'

This uses the string syntax: group:name:version. You can also use a Map-based syntax:

runtime group: 'com.mysql',
        name: 'mysql-connector-java',
        version: '5.1.16'

In Maven terminology, group corresponds to an artifact's groupId and name corresponds to its artifactId.

Multiple dependencies can be specified by passing multiple arguments:

runtime 'com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1'

// Or

runtime( [group:'com.mysql', name:'mysql-connector-java', version:'5.1.16'], [group:'net.sf.ehcache', name:'ehcache', version:'1.6.1'] )

Grails提供了如下5种依赖解析配置(或者是‘范围’):

  • build: 只在系统构建时的依赖
  • compile: 编译阶段时的依赖
  • runtime: 运行阶段的依赖,不包括编译阶段(见上解释)
  • test: 测试阶段的依赖,不包括运行阶段
  • provided: 开发阶段的依赖,不包括WAR部署阶段

dependencies代码块中,你可以通过同等的方法调用方式来指定一个依赖。比如你的应用中需要runtime的MySQL驱动,你可以这样处理:

runtime 'com.mysql:mysql-connector-java:5.1.16'

此处使用了字符串语法,其格式是:group:name:version,你也可以使用Map格式的语法:

runtime group: 'com.mysql',
        name: 'mysql-connector-java',
        version: '5.1.16'

对应于Maven术语,group跟工件(artifact)的groupId相对应,nameartifactId相对应。

多个依赖可以通过多参数方式来处理:

runtime 'com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1'

// Or

runtime( [group:'com.mysql', name:'mysql-connector-java', version:'5.1.16'], [group:'net.sf.ehcache', name:'ehcache', version:'1.6.1'] )

Disabling transitive dependency resolution

By default, Grails will not only get the JARs and plugins that you declare, but it will also get their transitive dependencies. This is usually what you want, but there are occasions where you want a dependency without all its baggage. In such cases, you can disable transitive dependency resolution on a case-by-case basis:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    transitive = false
}

// Or runtime group:'com.mysql', name:'mysql-connector-java', version:'5.1.16', transitive:false

禁用依赖解析的传递性

缺省情况下,Grails不仅仅获取你直接声明的JAR和插件,还包含其间接所依赖的。多数情况下,这正是你所需要的,不过在个别情况下,你并不需要这种传递性的依赖。这时,你可以有针对地禁止传递依赖,比如:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    transitive = false
}

// Or runtime group:'com.mysql', name:'mysql-connector-java', version:'5.1.16', transitive:false

Excluding specific transitive dependencies

A far more common scenario is where you want the transitive dependencies, but some of them cause issues with your own dependencies or are unnecessary. For example, many Apache projects have 'commons-logging' as a transitive dependency, but it shouldn't be included in a Grails project (we use SLF4J). That's where the excludes option comes in:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    excludes "xml-apis", "commons-logging"
}

// Or runtime(group:'com.mysql', name:'mysql-connector-java', version:'5.1.16') { excludes([ group: 'xml-apis', name: 'xml-apis'], [ group: 'org.apache.httpcomponents' ], [ name: 'commons-logging' ])

As you can see, you can either exclude dependencies by their artifact ID (also known as a module name) or any combination of group and artifact IDs (if you use the Map notation). You may also come across exclude as well, but that can only accept a single string or Map:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    exclude "xml-apis"
}

排除特定的依赖

传递依赖对你来说是如此的常用,但也有会跟你自己的依赖冲突或者重复的情况,比如很多的Apache项目都有依赖于'commons-logging',但是它不能被包含于Grails工程(其使用的是SLF4J)。因此就产生了excludes选项,比如:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    excludes "xml-apis", "commons-logging"
}

// Or runtime(group:'com.mysql', name:'mysql-connector-java', version:'5.1.16') { excludes([ group: 'xml-apis', name: 'xml-apis'], [ group: 'org.apache.httpcomponents' ], [ name: 'commons-logging' ])

如你所见,你可以通过工件ID(又名模块名称)或者组名加工件ID的方式来排除特定的依赖。你也可以通过exclude来排除,不过此处只能接收一个字符串或者Map:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    exclude "xml-apis"
}

Using Ivy module configurations

If you use Ivy module configurations and wish to depend on a specific configuration of a module, you can use the dependencyConfiguration method to specify the configuration to use.

provided("my.org:web-service:1.0") {
    dependencyConfiguration "api"
}

If the dependency configuration is not explicitly set, the configuration named "default" will be used (which is also the correct value for dependencies coming from Maven style repositories).

使用Ivy模块配置

如果你是使用Ivy的模块配置,并且希望依赖于某一特定模块,你可以使用dependencyConfiguration方法来指定:

provided("my.org:web-service:1.0") {
    dependencyConfiguration "api"
}

如果依赖配置没有明确指定,那么将被使用名为"default"缺省配置(其也可以兼容来自Maven风格的存储仓库)。

Where are the JARs?

With all these declarative dependencies, you may wonder where all the JARs end up. They have to go somewhere after all. By default Grails puts them into a directory, called the dependency cache, that resides on your local file system at user.home/.grails/ivy-cache. You can change this either via the settings.groovy file:

grails.dependency.cache.dir = "${userHome}/.my-dependency-cache"

or in the dependency DSL:

grails.project.dependency.resolution = {
    …
    cacheDir "target/ivy-cache"
    …
}

The settings.groovy option applies to all projects, so it's the preferred approach.

JAR在哪里?

对于所有声明的依赖,你可能好奇,这些JAR都到哪里去了?它们总要有个地方来保存的。缺省情况下,Grails将它们放到依赖缓存的目录,其位于你本地文件系统中user.home/.grails/ivy-cache。你也可以通过settings.groovy来修改,比如:

grails.dependency.cache.dir = "${userHome}/.my-dependency-cache"

或者使用依赖DSL:

grails.project.dependency.resolution = {
    …
    cacheDir "target/ivy-cache"
    …
}

settings.groovy的选项将应用于所有的工程,因为它是最优先使用的。

3.7.2 依赖存储库

Remote Repositories

Initially your BuildConfig.groovy does not use any remote public Maven repositories. There is a default grailsHome() repository that will locate the JAR files Grails needs from your Grails installation. To use a public repository, specify it in the repositories block:

repositories {
    mavenCentral()
}

In this case the default public Maven repository is specified. To use the SpringSource Enterprise Bundle Repository you can use the ebr() method:

repositories {
    ebr()
}

You can also specify a specific Maven repository to use by URL:

repositories {
    mavenRepo "http://repository.codehaus.org"
}

and even give it a name:

repositories {
    mavenRepo name: "Codehaus", root: "http://repository.codehaus.org"
}

so that you can easily identify it in logs.

远程存储仓库

刚创建的BuildConfig.groovy文件并没有使用任何远程的公共Maven存储库,在那里只有一个缺省的 grailsHome()用以从Grails安装目录定位所需要的JAR文件。要使用远程的公共存储库,请使用 repositories代码块,比如:

repositories {
    mavenCentral()
}

在上述示例中,指定的是Maven缺省的公共存储库。要使用SpringSource的企业包存储库,你可以使用ebr()方法:

repositories {
    ebr()
}

你还可以使用URL的方式来指定Maven存储库:

repositories {
    mavenRepo "http://repository.codehaus.org"
}

并且可以给其命名:

repositories {
    mavenRepo name: "Codehaus", root: "http://repository.codehaus.org"
}

这样你就可以在日志中轻易地辨别它们。

Controlling Repositories Inherited from Plugins

A plugin you have installed may define a reference to a remote repository just as an application can. By default your application will inherit this repository definition when you install the plugin.

If you do not wish to inherit repository definitions from plugins then you can disable repository inheritance:

repositories {
    inherit false
}

In this case your application will not inherit any repository definitions from plugins and it is down to you to provide appropriate (possibly internal) repository definitions.

控制插件存储库地继承

通常,你安装的插件会定义一个远程的存储库,缺省情况下,而你的应用将继承你安装插件中的存储库的定义。

如果你不希望继承来自插件的存储库定义,你可以禁止这种存储库的继承:

repositories {
    inherit false
}

上述示例中,应用将不继承任何插件的存储库的定义,而是仅仅依赖于你定义的存储库。

Offline Mode

There are times when it is not desirable to connect to any remote repositories (whilst working on the train for example!). In this case you can use the offline flag to execute Grails commands and Grails will not connect to any remote repositories:

grails --offline run-app

Note that this command will fail if you do not have the necessary dependencies in your local Ivy cache

You can also globally configure offline mode by setting grails.offline.mode to true in ~/.grails/settings.groovy or in your project's BuildConfig.groovy file:

grails.offline.mode=true

离线模式

偶尔的时候,你将不能访问任何远程的存储库(比如在火车上工作时)。此种情况下,你可以使用offline标志来执行Grails命令,这样Grails将不会连接任何远程存储库,比如:

grails --offline run-app

注意:如果你本地Ivy缓存中没有所需的依赖,上述命令将会出错。

你还可以通过设置~/.grails/settings.groovy或者工程中BuildConfig.groovy文件中的grails.offline.modetrue的方式,将其配置为全局离线模式:

grails.offline.mode=true

Local Resolvers

If you do not wish to use a public Maven repository you can specify a flat file repository:

repositories {
    flatDir name:'myRepo', dirs:'/path/to/repo'
}

To specify your local Maven cache (~/.m2/repository) as a repository:

repositories {
    mavenLocal()
}

本地解析器

如果你不希望使用远程的Maven存储库,你可以指定一个平面文件(flat file)存储器:

repositories {
    flatDir name:'myRepo', dirs:'/path/to/repo'
}

将你本地的Maven缓存作为(~/.m2/repository)作为存储器,可以用下面的处理:

repositories {
    mavenLocal()
}

Custom Resolvers

If all else fails since Grails builds on Apache Ivy you can specify an Ivy resolver:

/*
 * Configure our resolver.
 */
def libResolver = new org.apache.ivy.plugins.resolver.URLResolver()
['libraries', 'builds'].each {

libResolver.addArtifactPattern( "http://my.repository/${it}/" + "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]")

libResolver.addIvyPattern( "http://my.repository/${it}/" + "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]") }

libResolver.name = "my-repository" libResolver.settings = ivySettings

resolver libResolver

It's also possible to pull dependencies from a repository using SSH. Ivy comes with a dedicated resolver that you can configure and include in your project like so:

import org.apache.ivy.plugins.resolver.SshResolver
…
repositories {
    ...

def sshResolver = new SshResolver( name: "myRepo", user: "username", host: "dev.x.com", keyFile: new File("/home/username/.ssh/id_rsa"), m2compatible: true)

sshResolver.addArtifactPattern( "/home/grails/repo/[organisation]/[artifact]/" + "[revision]/[artifact]-[revision].[ext]")

sshResolver.latestStrategy = new org.apache.ivy.plugins.latest.LatestTimeStrategy()

sshResolver.changingPattern = ".*SNAPSHOT"

sshResolver.setCheckmodified(true)

resolver sshResolver }

Download the JSch JAR and add it to Grails' classpath to use the SSH resolver. You can do this by passing the path in the Grails command line:

grails -classpath /path/to/jsch compile|run-app|etc.

You can also add its path to the CLASSPATH environment variable but be aware this it affects many Java applications. An alternative on Unix is to create an alias for grails -classpath ... so that you don't have to type the extra arguments each time.

自定义解析器

如果上述的都失败了,你还可以自定义一个Ivy解析器,因为Grails是基于Apache Ivy构建的:

/*
 * Configure our resolver.
 */
def libResolver = new org.apache.ivy.plugins.resolver.URLResolver()
['libraries', 'builds'].each {

libResolver.addArtifactPattern( "http://my.repository/${it}/" + "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]")

libResolver.addIvyPattern( "http://my.repository/${it}/" + "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]") }

libResolver.name = "my-repository" libResolver.settings = ivySettings

resolver libResolver

此外还可以通过SSH的方式从存储库中获取依赖。为此Ivy提供了一个专门的用于配置的解析器,在你的工程看起来可能如下:

import org.apache.ivy.plugins.resolver.SshResolver
…
repositories {
    ...

def sshResolver = new SshResolver( name: "myRepo", user: "username", host: "dev.x.com", keyFile: new File("/home/username/.ssh/id_rsa"), m2compatible: true)

sshResolver.addArtifactPattern( "/home/grails/repo/[organisation]/[artifact]/" + "[revision]/[artifact]-[revision].[ext]")

sshResolver.latestStrategy = new org.apache.ivy.plugins.latest.LatestTimeStrategy()

sshResolver.changingPattern = ".*SNAPSHOT"

sshResolver.setCheckmodified(true)

resolver sshResolver }

要使用SSH解析器,先要下载JSch的JAR到你Grails的类路径(classpath)中,你可以通过命令行的方式来设置,比如:

grails -classpath /path/to/jsch compile|run-app|etc.

你也可以将路径添加到CLASSPATH环境变量中,不要要注意,这可能会影响很多的Java应用。在Unix下,你可以为grails -classpath ...创建一个别名,这样就可以不需要每次都敲额外的参数了。

Authentication

If your repository requires authentication you can configure this using a credentials block:

credentials {
    realm = ".."
    host = "localhost"
    username = "myuser"
    password = "mypass"
}

This can be placed in your USER_HOME/.grails/settings.groovy file using the grails.project.ivy.authentication setting:

grails.project.ivy.authentication = {
    credentials {
        realm = ".."
        host = "localhost"
        username = "myuser"
        password = "mypass"
    }
}

验证

如果你的存储仓库需要验证,那么你可以通过credentials代码块来进行配置,比如:

credentials {
    realm = ".."
    host = "localhost"
    username = "myuser"
    password = "mypass"
}

你也可以通过设置USER_HOME/.grails/settings.groovy文件中的grails.project.ivy.authentication来实现:

grails.project.ivy.authentication = {
    credentials {
        realm = ".."
        host = "localhost"
        username = "myuser"
        password = "mypass"
    }
}

3.7.3 调试解析

If you are having trouble getting a dependency to resolve you can enable more verbose debugging from the underlying engine using the log method:

// log level of Ivy resolver, either 'error', 'warn',
// 'info', 'debug' or 'verbose'
log "warn"

A common issue is that the checksums for a dependency don't match the associated JAR file, and so Ivy rejects the dependency. This helps ensure that the dependencies are valid. But for a variety of reasons some dependencies simply don't have valid checksums in the repositories, even if they are valid JARs. To get round this, you can disable Ivy's dependency checks like so:

grails.project.dependency.resolution = {
    …
    log "warn"
    checksums false
    …
}

This is a global setting, so only use it if you have to.

如果你解析依赖遇到了问题的话,你可以使用基本的log方法来显示更多的调试信息:

// log level of Ivy resolver, either 'error', 'warn',
// 'info', 'debug' or 'verbose'
log "warn"

一个常见的问题是依赖的校验码跟其相应的JAR文件不匹配,这将导致Ivy不接收此依赖。此举用以保证依赖的有效性,但是由于不确定的原因,导致有些依赖在存储仓库中即使存在有效的JAR,也还是没有有效的校验码。要解决此问题,你可以禁用Ivy的依赖检测,比如:

grails.project.dependency.resolution = {
    …
    log "warn"
    checksums false
    …
}

这是个全局设置,因此请在不得已的时候才使用。

3.7.4 依赖继承

By default every Grails application inherits several framework dependencies. This is done through the line:

inherits "global"

Inside the BuildConfig.groovy file. To exclude specific inherited dependencies you use the excludes method:

inherits("global") {
    excludes "oscache", "ehcache"
}

缺省情况下,每一个Grails应用都继承多个框架依赖,配置如下:

inherits "global"

BuildConfig.groovy文件中,你可以通过excludes方法来排除掉指定的依赖,比如:

inherits("global") {
    excludes "oscache", "ehcache"
}

3.7.5 缺省的依赖

Most Grails applications have runtime dependencies on several jar files that are provided by the Grails framework. These include libraries like Spring, Sitemesh, Hibernate etc. When a war file is created, all of these dependencies will be included in it. But, an application may choose to exclude these jar files from the war. This is useful when the jar files will be provided by the container, as would normally be the case if multiple Grails applications are deployed to the same container.

The dependency resolution DSL provides a mechanism to express that all of the default dependencies will be provided by the container. This is done by invoking the defaultDependenciesProvided method and passing true as an argument:

grails.project.dependency.resolution = {

defaultDependenciesProvided true // all of the default dependencies will // be "provided" by the container

inherits "global" // inherit Grails' default dependencies

repositories { grailsHome() … } dependencies { … } }

defaultDependenciesProvided must come before inherits, otherwise the Grails dependencies will be included in the war.

大部分的Grails应用依赖于Grails框架自带的一些jar文件,比如Spring、Sitemesh和Hibernate等等。当创建一个war文件的时候。但是应用还是可以选择从war中排除这些jar文件的。这非常适合于本身容器已经包含这些jar文件的情况,尤其多个Grails应用部署于这样同一容器中。

解析依赖的DSL就有让容器提供缺省依赖的选项,这可以通过传递truedefaultDependenciesProvided方法来实现:

grails.project.dependency.resolution = {

defaultDependenciesProvided true // all of the default dependencies will // be "provided" by the container

inherits "global" // inherit Grails' default dependencies

repositories { grailsHome() … } dependencies { … } }

defaultDependenciesProvided必须位于inherits之前,否则Grails的依赖还是会被包含到war中。

3.7.6 快照和其他变化的依赖

Typically, dependencies are constant. That is, for a given combination of group, name and version the jar (or plugin) that it refers to will never change. The Grails dependency management system uses this fact to cache dependencies in order to avoid having to download them from the source repository each time. Sometimes this is not desirable. For example, many developers use the convention of a snapshot (i.e. a dependency with a version number ending in “-SNAPSHOT”) that can change from time to time while still retaining the same version number. We call this a "changing dependency".

Whenever you have a changing dependency, Grails will always check the remote repository for a new version. More specifically, when a changing dependency is encountered during dependency resolution its last modified timestamp in the local cache is compared against the last modified timestamp in the dependency repositories. If the version on the remote server is deemed to be newer than the version in the local cache, the new version will be downloaded and used.

{info} Be sure to read the next section on “Dependency Resolution Caching” in addition to this one as it affects changing dependencies. {info}

All dependencies (jars and plugins) with a version number ending in -SNAPSHOT are implicitly considered to be changing by Grails. You can also explicitly specify that a dependency is changing by setting the changing flag in the dependency DSL:

runtime ('org.my:lib:1.2.3') {
    changing = true
}

There is a caveat to the support for changing dependencies that you should be aware of. Grails will stop looking for newer versions of a dependency once it finds a remote repository that has the dependency.

Consider the following setup:

grails.project.dependency.resolution = {
    repositories {
        mavenLocal()
        mavenRepo "http://my.org/repo"
    }
    dependencies {
        compile "myorg:mylib:1.0-SNAPSHOT"
    }

In this example we are using the local maven repository and a remote network maven repository. Assuming that the local Grails dependency and the local Maven cache do not contain the dependency but the remote repository does, when we perform dependency resolution the following actions will occur:

  • maven local repository is searched, dependency not found
  • maven network repository is searched, dependency is downloaded to the cache and used

Note that the repositories are checked in the order they are defined in the BuildConfig.groovy file.

If we perform dependency resolution again without the dependency changing on the remote server, the following will happen:

  • maven local repository is searched, dependency not found
  • maven network repository is searched, dependency is found to be the same “age” as the version in the cache so will not be updated (i.e. downloaded)

Later on, a new version of mylib 1.0-SNAPSHOT is published changing the version on the server. The next time we perform dependency resolution, the following will happen:

  • maven local repository is searched, dependency not found
  • maven network repository is searched, dependency is found to newer than version in the cache so will be updated (i.e. downloaded to the cache)

So far everything is working well.

Now we want to test some local changes to the mylib library. To do this we build it locally and install it to the local Maven cache (how doesn't particularly matter). The next time we perform a dependency resolution, the following will occur:

  • maven local repository is searched, dependency is found to newer than version in the cache so will be updated (i.e. downloaded to the cache)
  • maven network repository is NOT searched as we've already found the dependency

This is what we wanted to occur.

Later on, a new version of mylib 1.0-SNAPSHOT is published changing the version on the server. The next time we perform dependency resolution, the following will happen:

  • maven local repository is searched, dependency is found to be the same “age” as the version in the cache so will not be updated (i.e. downloaded)
  • maven network repository is NOT searched as we've already found the dependency

This is likely to not be the desired outcome. We are now out of sync with the latest published snapshot and will continue to keep using the version from the local maven repository.

The rule to remember is this: when resolving a dependency, Grails will stop searching as soon as it finds a repository that has the dependency at the specified version number. It will not continue searching all repositories trying to find a more recently modified instance.

To remedy this situation (i.e. build against the newer version of mylib 1.0-SNAPSHOT in the remote repository), you can either:

  • Delete the version from the local maven repository, or
  • Reorder the repositories in the BuildConfig.groovy file

Where possible, prefer deleting the version from the local maven repository. In general, when you have finished building against a locally built SNAPSHOT always try to clear it from the local maven repository.

This changing dependency behaviour is an unmodifiable characteristic of the underlying dependency management system that Grails uses, Apache Ivy. It is currently not possible to have Ivy search all repositories to look for newer versions (in terms of modification date) of the same dependency (i.e. the same combination of group, name and version).

通常情况下,依赖是不变的。或者说,对于给定了groupnameversion组合的jar(或者插件)来说,就意味着将永远不变。Grails的依赖管理系统就是利用这点来缓存依赖以避免每次从远程存储库中下载。但有时候这并不是可取的。比如很多开发者使用的是 快照 (例如依赖一个以“-SNAPSHOT”结尾的版本号)规约,这样就是在同一个版本下,也可以一次一次的更新变化。我们称之为“变化的依赖”。

每当你有一个变化依赖的时候,Grails将总是从远程存储库中检查新版本。更确切地说,当依赖解析处理一个变化依赖时,其本地缓存的最新时间戳将跟依赖存储库中的最新时间戳比较。如果远程服务器的版本比本地缓存的新,那么新的版本将被下载和使用。

{info} 除此之外,请务必阅读下一节的“依赖解析缓存”,因为它将影响变化的依赖。 {info}

所有版本号以-SNAPSHOT结尾的依赖(jar和插件),Grails将 隐含地 将其视为变化的。你也可以通过changing标记来显式的在依赖DSL中指定变化的依赖:

runtime ('org.my:lib:1.2.3') {
    changing = true
}

对变化依赖的支持,有一个点你需要知道,那就是一旦在依赖的远程存储库中存在了,那么Grails将会停止查找依赖的新版本。

以如下的设置为例:

grails.project.dependency.resolution = {
    repositories {
        mavenLocal()
        mavenRepo "http://my.org/repo"
    }
    dependencies {
        compile "myorg:mylib:1.0-SNAPSHOT"
    }

在此示例中,我们将使用maven的本地存储库和一个远程网络存储库。假设一个依赖不在Grails的本地依赖和Maven的本地缓存中,而是在其远程存储库中,那么当我们执行依赖解析的时候,将会发生如下的动作:

  • 在maven的本地存储库中查找,依赖没有找到
  • 在maven的网络存储库中查找,依赖将被下载到本地缓存中并且使用它

要提醒的是,存储库的检查顺序是定义在BuildConfig.groovy文件中的。

如果远程服务器依赖没有变化,我们再执行依赖解析的话,将发生如下情况:

  • 在maven的本地存储库中查找,依赖没有找到
  • 在maven的网络存储库中查找,依赖被发现跟本地缓存是同一个版本,因此也就不会更新(比如下载)

再后来,一个新版本的mylib 1.0-SNAPSHOT被发布到服务器上。接下来我们再执行依赖解析,将会发生如下情况:

  • 在maven的本地存储库中查找,依赖没有找到
  • 在maven的网络存储库中查找,依赖被发现比本地缓存的版本新,因此执行更新操作(比如将其下载到本地缓存中)

到目前为止,一切都很顺利。

现在我们想测试mylib库的一些本地变动。要完成此事,我们需要在本地构建并且将其安装到Maven的本地缓存中(如果没有什么特别问题的话)。接下来,我们执行依赖解析,将发生如下情况:

  • 在maven的本地存储库中查找,依赖被发现比本地缓存的版本新,因此执行更新操作(比如将其下载到本地缓存中)
  • 不再查找maven的网络存储库,因为我们已经找到了依赖

这正是我们期望发生的。

再继续,在服务器上发布一个变化的新版本mylib 1.0-SNAPSHOT。然后,我们执行依赖解析,将发生如下情况:

  • 在maven的本地存储库中查找,依赖被发现跟本地缓存是同一个版本,因此也就不会更新(比如下载)
  • 不再查找maven的网络存储库,因为我们已经找到了依赖

这可能就不是一个理想的结果了。我们现在不再同步已经最新发布的快照了,并且将继续使用maven的本地存储库中的版本。

因此要记住这条规则:在解析一个依赖的时候,如果在一个存储库中找到了一个给定版本的依赖,那么Grails将会停止其搜索。因此也就 不会 继续搜索全部的存储库以查找一个最新修改的依赖实例。

要补救这个问题(比如远程存储库中mylib 1.0-SNAPSHOT 版本构建问题),你可以有下边两个选择:

  • 删除maven的本地存储库中的版本,或者
  • BuildConfig.groovy文件中的repositories重新排序

如果可能,推荐采用删除maven的本地存储库中的版本的方式。总的来说,当你结束基于SNAPSHOT的本地构建时,应该尽量将其从maven的本地存储库中清除。

此变化依赖的行为是Apache Ivy(是Grails的依赖管理系统基础)一个不可变特性。目前,Ivy是不可能搜索全部的存储库来寻找同一个依赖(比如相同groupnameversion的组合)的最新版本(修改时间的同名词)。

3.7.7 依赖报告

As mentioned in the previous section a Grails application consists of dependencies inherited from the framework, the plugins installed and the application dependencies itself.

To obtain a report of an application's dependencies you can run the dependency-report command:

grails dependency-report

By default this will generate reports in the target/dependency-report directory. You can specify which configuration (scope) you want a report for by passing an argument containing the configuration name:

grails dependency-report runtime

正如前面提到的,Grails应用所依赖的主要来自系统框架,已经安装的插件和应用自身的依赖。

要获取应用的依赖情况报告,可以运行dependency-report命令:

grails dependency-report

缺省情况下,生成的报告位于target/dependency-report目录下。你也可以通过传递一个配置(又称范围)名称来生成特定的报告,比如:

grails dependency-report runtime

3.7.8 插件的JAR依赖

Specifying Plugin JAR dependencies

The way in which you specify dependencies for a plugin is identical to how you specify dependencies in an application. When a plugin is installed into an application the application automatically inherits the dependencies of the plugin.

To define a dependency that is resolved for use with the plugin but not exported to the application then you can set the export property of the dependency:

test('org.spockframework:spock-core:0.5-groovy-1.8') {
    export = false
}

In this case the Spock dependency will be available only to the plugin and not resolved as an application dependency. Alternatively, if you're using the Map syntax:

test group: 'org.spockframework', name: 'spock-core',
     version: '0.5-groovy-1.8', export: false

You can use exported = false instead of export = false, but we recommend the latter because it's consistent with the Map argument.

指定插件的JAR依赖

为一个插件指定依赖的方式跟你为一个应用的方式一致。当一个插件被安装以后,此应用将自动继承插件的依赖。

如果希望插件的依赖 不要导出 到应用中,可以通过设置export属性来完成:

test('org.spockframework:spock-core:0.5-groovy-1.8') {
    export = false
}

上述示例,所依赖的Spock只在插件中有效,在应用中将不会被解析。你也可以使用Map的语法来设置:

test group: 'org.spockframework', name: 'spock-core',
     version: '0.5-groovy-1.8', export: false

你也可以使用exported = false来替代export = false,但是我们推荐你使用后者,因为这样可以跟Map参数保持一致。

Overriding Plugin JAR Dependencies in Your Application

If a plugin is using a JAR which conflicts with another plugin, or an application dependency then you can override how a plugin resolves its dependencies inside an application using exclusions. For example:

plugins {
    compile(":hibernate:$grailsVersion") {
        excludes "javassist"
    }
}

dependencies { runtime "javassist:javassist:3.4.GA" }

In this case the application explicitly declares a dependency on the "hibernate" plugin and specifies an exclusion using the excludes method, effectively excluding the javassist library as a dependency.

在应用中覆盖插件依赖

如果一个插件使用到的JAR依赖跟另外一个插件或者应用的依赖相冲突,你可以使用排除加覆盖的方式来解决,比如:

plugins {
    compile(":hibernate:$grailsVersion") {
        excludes "javassist"
    }
}

dependencies { runtime "javassist:javassist:3.4.GA" }

在上述依赖"hibernate"插件的应用中,你可以通过excludes方法排除掉javassist框架,而后另外声明其依赖。

3.7.9 Maven集成

When using the Grails Maven plugin, Grails' dependency resolution mechanics are disabled as it is assumed that you will manage dependencies with Maven's pom.xml file.

However, if you would like to continue using Grails regular commands like run-app, test-app and so on then you can tell Grails' command line to load dependencies from the Maven pom.xml file instead.

To do so simply add the following line to your BuildConfig.groovy:

grails.project.dependency.resolution = {
    pom true
    ..
}

The line pom true tells Grails to parse Maven's pom.xml and load dependencies from there.

当使用Grails的Maven插件时,Grails自带的依赖解析机制将被禁止,因为其假设你已经使用Maven的pom.xml来管理依赖了。

即使这样,你如果想继续使用Grails的常规命令比如run-apptest-app等等,你还是可以通过其命令行从Maven的pom.xml加载依赖的。

你只需要在BuildConfig.groovy文件中简单加入如下即可:

grails.project.dependency.resolution = {
    pom true
    ..
}

pom true就是用来告诉Grails要使用Maven的pom.xml来加载依赖。

3.7.10 部署到Maven存储库

If you use Maven to build your Grails project, you can use the standard Maven targets mvn install and mvn deploy. If not, you can deploy a Grails project or plugin to a Maven repository using the maven-publisher plugin.

The plugin provides the ability to publish Grails projects and plugins to local and remote Maven repositories. There are two key additional targets added by the plugin:

  • maven-install - Installs a Grails project or plugin into your local Maven cache
  • maven-deploy - Deploys a Grails project or plugin to a remote Maven repository

By default this plugin will automatically generate a valid pom.xml for you unless a pom.xml is already present in the root of the project, in which case this pom.xml file will be used.

如果你是使用Maven来构建你的Grails工程,那么你可以使用标准的Maven目标命令(target)mvn installmvn deploy来安装部署。 如果不是,你可以通过maven-publisher插件来将Grails工程或者插件部署到Maven存储库。

此插件能够将Grails工程和插件发布到本地和远程的Maven存储库,下面是此插件额外的目标命令:

  • maven-install - 安装Grails工程或者插件到你本地的Maven缓存中
  • maven-deploy - 部署Grails工程或者插件到远程的Maven存储库中

如果你工程的根目录下边没有pom.xml,此插件将为你自动生成此文件,否则系统将直接使用已经存在的pom.xml文件。

maven-install

The maven-install command will install the Grails project or plugin artifact into your local Maven cache:

grails maven-install

In the case of plugins, the plugin zip file will be installed, whilst for application the application WAR file will be installed.

maven-install

maven-install命令将安装Grails工程或者插件到你本地的Maven缓存:

grails maven-install

当工程是插件时,将安装成zip文件;当是应用时,将安装成WAR文件。

maven-deploy

The maven-deploy command will deploy a Grails project or plugin into a remote Maven repository:

grails maven-deploy

It is assumed that you have specified the necessary <distributionManagement> configuration within a pom.xml or that you specify the id of the remote repository to deploy to:

grails maven-deploy --repository=myRepo

The repository argument specifies the 'id' for the repository. Configure the details of the repository specified by this 'id' within your grails-app/conf/BuildConfig.groovy file or in your $USER_HOME/.grails/settings.groovy file:

grails.project.dependency.distribution = {
    localRepository = "/path/to/my/local"
    remoteRepository(id: "myRepo", url: "http://myserver/path/to/repo")
}

The syntax for configuring remote repositories matches the syntax from the remoteRepository element in the Ant Maven tasks. For example the following XML:

<remoteRepository id="myRepo" url="scp://localhost/www/repository">
    <authentication username="..." privateKey="${user.home}/.ssh/id_dsa"/>
</remoteRepository>

Can be expressed as:

remoteRepository(id: "myRepo", url: "scp://localhost/www/repository") {
    authentication username: "...", privateKey: "${userHome}/.ssh/id_dsa"
}

By default the plugin will try to detect the protocol to use from the URL of the repository (ie "http" from "http://.." etc.), however to specify a different protocol you can do:

grails maven-deploy --repository=myRepo --protocol=webdav

The available protocols are:

  • http
  • scp
  • scpexe
  • ftp
  • webdav

maven-deploy

maven-deploy命令将Grails工程或者插件发布到远程的Maven存储库:

grails maven-deploy

上述示例的前提是你已经配置了pom.xml文件的<distributionManagement>,或者你可以通过指定远程存储库id的方式,比如:

grails maven-deploy --repository=myRepo

repository参数是存储库的'id',此'id'的详细配置位于grails-app/conf/BuildConfig.groovy或者$USER_HOME/.grails/settings.groovy文件中:

grails.project.dependency.distribution = {
    localRepository = "/path/to/my/local"
    remoteRepository(id: "myRepo", url: "http://myserver/path/to/repo")
}

配置远程存储库的语法跟Ant Maven任务的remoteRepository元素相对应,如下XML所示:

<remoteRepository id="myRepo" url="scp://localhost/www/repository">
    <authentication username="..." privateKey="${user.home}/.ssh/id_dsa"/>
</remoteRepository>

可以对应为:

remoteRepository(id: "myRepo", url: "scp://localhost/www/repository") {
    authentication username: "...", privateKey: "${userHome}/.ssh/id_dsa"
}

缺省条件下,此插件通过存储库的URL来自动检测协议的(比如"http://.."是"http"),不过你还是可以指定另外一个不同的协议的:

grails maven-deploy --repository=myRepo --protocol=webdav

支持的有效协议如下:

  • http
  • scp
  • scpexe
  • ftp
  • webdav

Groups, Artifacts and Versions

Maven defines the notion of a 'groupId', 'artifactId' and a 'version'. This plugin pulls this information from the Grails project conventions or plugin descriptor.

Projects

For applications this plugin will use the Grails application name and version provided by Grails when generating the pom.xml file. To change the version you can run the set-version command:

grails set-version 0.2

The Maven groupId will be the same as the project name, unless you specify a different one in Config.groovy:

grails.project.groupId="com.mycompany"

组,工件和版本

在Maven中,定义了'groupId','artifactId'和'version'概念,Maven插件会将Grails工程或者插件的描述转换为这些相应的定义。

工程

对于一个应用来说,当要生成pom.xml文件的时候,此插件会根据Grails应用提供的名称和版本进行生成。要修改版本信息,可以运行set-version命令来完成:

grails set-version 0.2

缺省情况下,Maven的groupId跟其工程名称一样,不过你可以在Config.groovy中指定,比如:

grails.project.groupId="com.mycompany"

Plugins

With a Grails plugin the groupId and version are taken from the following properties in the *GrailsPlugin.groovy descriptor:

String groupId = 'myOrg'
String version = '0.1'

The 'artifactId' is taken from the plugin name. For example if you have a plugin called FeedsGrailsPlugin the artifactId will be "feeds". If your plugin does not specify a groupId then this defaults to "org.grails.plugins".

插件

对于一个Grails插件来说,其groupIdversion来自于*GrailsPlugin.groovy文件的描述属性:

String groupId = 'myOrg'
String version = '0.1'

'artifactId'来自于插件的名称。比如你有一个插件是FeedsGrailsPlugin那么其artifactId是"feeds"。如果你的插件没有指定groupId那么其缺省值为"org.grails.plugins"。

3.7.11 插件依赖

As of Grails 1.3 you can declaratively specify plugins as dependencies via the dependency DSL instead of using the install-plugin command:

grails.project.dependency.resolution = {
    …
    repositories {
        …
    }

plugins { runtime ':hibernate:1.2.1' }

dependencies { … } … }

If you don't specify a group id the default plugin group id of org.grails.plugins is used. You can specify to use the latest version of a particular plugin by using "latest.integration" as the version number:

plugins {
    runtime ':hibernate:latest.integration'
}

从Grails 1.3以来,你就可以通过依赖DSL的方式来声明插件的依赖了,而非只用install-plugin命令:

grails.project.dependency.resolution = {
    …
    repositories {
        …
    }

plugins { runtime ':hibernate:1.2.1' }

dependencies { … } … }

如果你没有指定插件的组ID,其缺省值是org.grails.plugins。你还可以将"latest.integration"作为版本号,这样就会自动获取最新的版本,例如:

plugins {
    runtime ':hibernate:latest.integration'
}

Integration vs. Release

The "latest.integration" version label will also include resolving snapshot versions. To not include snapshot versions then use the "latest.release" label:

plugins {
    runtime ':hibernate:latest.release'
}

The "latest.release" label only works with Maven compatible repositories. If you have a regular SVN-based Grails repository then you should use "latest.integration".

And of course if you use a Maven repository with an alternative group id you can specify a group id:

plugins {
    runtime 'mycompany:hibernate:latest.integration'
}

集成(Integration)和正式(Release)版本

"latest.integration"的版本标签将会解析包含快照的版本,如果不想,可以使用"latest.release"标签:

plugins {
    runtime ':hibernate:latest.release'
}

"latest.release"标签仅仅用于兼容Maven的存储库。如果你使用基于SVN的Grails存储库,你应该使用"latest.integration"。

当然如果你使用非缺省组ID的Maven存储库,你可以为其指定一个:

plugins {
    runtime 'mycompany:hibernate:latest.integration'
}

Plugin Exclusions

You can control how plugins transitively resolves both plugin and JAR dependencies using exclusions. For example:

plugins {
    runtime(':weceem:0.8') {
        excludes "searchable"
    }
}

Here we have defined a dependency on the "weceem" plugin which transitively depends on the "searchable" plugin. By using the excludes method you can tell Grails not to transitively install the searchable plugin. You can combine this technique to specify an alternative version of a plugin:

plugins {
    runtime(':weceem:0.8') {
        excludes "searchable" // excludes most recent version
    }
    runtime ':searchable:0.5.4' // specifies a fixed searchable version
}

You can also completely disable transitive plugin installs, in which case no transitive dependencies will be resolved:

plugins {
    runtime(':weceem:0.8') {
        transitive = false
    }
    runtime ':searchable:0.5.4' // specifies a fixed searchable version
}

插件的排除

通过使用排除,你可以控制插件如何传递地解析JAR和插件的依赖,比如:

plugins {
    runtime(':weceem:0.8') {
        excludes "searchable"
    }
}

此处我们定义了一个名为"weceem"的插件,其又依赖于"searchable"插件。通过使用excludes方法,你可以告诉Grails 不以 传递的方式安装searchable插件。你可以在此种方式的基础上,另外单独安装另一个版本的插件:

plugins {
    runtime(':weceem:0.8') {
        excludes "searchable" // excludes most recent version
    }
    runtime ':searchable:0.5.4' // specifies a fixed searchable version
}

你也可以完全禁用插件的传递安装,此时那些传递的依赖将不再解析,比如:

plugins {
    runtime(':weceem:0.8') {
        transitive = false
    }
    runtime ':searchable:0.5.4' // specifies a fixed searchable version
}