1

在 Grails 中,您可以像这样在项目的 Config.groovy 文件中定义全局约束

grails.gorm.default.constraints = {
    myShared(nullable: false, blank: false)
}

并在域内像这样使用它们

static constraints = {
    name(shared: "myShared")
}

由于我们的领域类在多个 Grails 项目中被重用,它们被拆分为插件。插件的 Config.groovy 文件被排除在外,因此在那里定义全局约束将不起作用。因此,我创建了一个 Constraints.groovy 文件,该文件被合并到包含域类的插件的插件描述符内的应用程序配置中。这可行,但我仍然在运行主项目(grails run-app)时遇到以下异常:

Caused by GrailsConfigurationException: Property [test.plugin.TestDomain.name] references shared constraint [myShared:null], which doesn't exist!

在 Grails 核心中进行一些调试后,我发现在运行插件描述符之前,域类已经使用共享约束进行了初始化。

public DefaultGrailsDomainClass(Class<?> clazz, Map<String, Object> defaultConstraints)

构造函数中的映射包含共享约束。如果我将全局约束放在主项目的 Config.groovy 文件中,它包含定义的约束并且一切正常。但是,如果我将它们合并到插件描述符中,则此映射为空并且抛出异常。

我的问题是是否可以在 Grails 插件中以某种方式定义全局约束?我可能错过了什么?将全局约束复制到每个 Grails 项目中不应成为解决方案。此外,首选不使用另一个插件来定义约束的解决方案。

顺便说一句,我们使用的是 Grails 2.2.4。

4

2 回答 2

1

考虑到 Sérgio 的回答,我进一步调试了一下,并提出了一个可能适用于某些项目的解决方案,而对于某些项目则不会。不幸的是,我们工作中的 Grails 项目属于后一组项目……但首先要做的是。这就是我所做的。

我设置了一个完全空的 Grails 项目和 Grails 插件项目。该插件包含在内联中,以反映我们在 Grails 项目中的工作情况。

我在插件的 conf 目录中创建了一个文件 Constraints.groovy ,如问题中所述。

grails.gorm.default.constraints = {
    myShared(nullable: false, blank: false)
}

为了让这些共享约束可用于插件内部的测试,我将此文件添加到 Config.groovy 中的配置位置。

grails.config.locations = [Constraints]

现在是使这些约束可用于主项目的部分。这是通过在插件描述符中添加一些行来实现的。

def loadBefore = ['domainClass']

正如 Sérgio 建议的那样,我更改了加载顺序。虽然我不确定这是否真的有必要。关于我为什么不确定的更多细节如下。

def doWithSpring = {
    ConstraintEvalUtils.clearDefaultConstraints()

    mergeConfig(application)
}

protected mergeConfig(application) {
    application.config.merge(loadConfig(application))
}

protected loadConfig(application) {
    new ConfigSlurper(Environment.current.name).parse(application.classLoader.loadClass("Constraints"))
}

这两个方法负责将 Constraints.groovy 的内容加载并合并到应用程序配置中。但实际上,诀窍是ConstraintEvalUtils.clearDefaultConstraints()在合并之前调用。

/**
 * Looks up the default configured constraints from the given configuration
 */
public static Map<String, Object> getDefaultConstraints(ConfigObject config) {
    def cid = System.identityHashCode(config)
    if (defaultConstraintsMap == null || configId != cid) {
        configId = cid
        def constraints = config?.grails?.gorm?.default?.constraints
        if (constraints instanceof Closure) {
            defaultConstraintsMap = new ClosureToMapPopulator().populate((Closure<?>) constraints);
        }
        else {
            defaultConstraintsMap = Collections.emptyMap()
        }
    }
    return defaultConstraintsMap
}

此方法(也在 中ConstraintEvalUtils)被调用以加载共享约束。如您所见,结果被缓存在defaultConstraintsMap. 因此,如果在第一次调用此方法时共享约束为空,它总是返回一个空映射。所以我调试了这个方法来找出这个方法实际被调用的时间。它总是返回一个空地图。即使我在插件描述符中设置loadBeforecore,该方法已经在插件描述符之前被调用(不止一次)。正如我已经提到ConstraintEvalUtils.clearDefaultConstraints()的那样,它可以清除缓存的约束,并且可以从我将约束合并到的配置中重新加载默认约束。

差不多就是这样。实际上需要添加的行并不多。它适用于我的测试项目,可能也适用于其他项目,但不适用于我们正在工作的 Grails 项目。如果我也能在那里工作,我会更新我的答案。

于 2013-10-25T23:18:30.833 回答
1

由于 Grails 在doWithSpring闭包中初始化约束,我认为你不能使用配置文件来完成。

但是,如果您查看DomainClassGrailsPlugin您可以访问配置对象。

def doWithSpring = {
  def config = application.config
  def defaultConstraintsMap = getDefaultConstraints(config)
  ...
}

所以我认为你可以做类似的事情(未经测试)

def loadBefore = ['domainClass']

def doWithSpring = {
  def config = application.config
  config.grails.gorm.default.constraints = {
    myShared(nullable: false, blank: false)
  }
}
于 2013-10-22T11:03:59.420 回答