如何使用Groovy闭包来包装所有Grails服务方法?

时间:2015-10-30 17:03:27

标签: grails groovy closures aop grails-services

Grails 2.4.x。

我要求由grails create-service <xyz>生成的所有Grails服务的所有方法都包含&#34; /截获以下逻辑:

try {
    executeTheMethod()
} catch(MyAppException maExc) {
    log.error(ExceptionUtils.getStackTrace(maExc))
    myAppExceptionHandler.handleOrRethrow(maExc)
}

其中:

  • log.error(...)是您使用@Slf4j注释注释班级时获得的SLF4J提供的记录器;和
  • ExceptionUtils是来自org.apache.commons:commons-lang3:3.4的人;和
  • myAppExceptionHandler的类型为com.example.myapp.MyAppExceptionHandler;和
  • 对于Grails服务中定义的每个方法,此行为存在(或者可以选择在需要以某种方式显式调用它的情况下存在)

显然,这个包装器代码也需要为这些类包含import语句。

例如,如果我有WidgetService,如下所示:

class WidgetService {
    WidgetDataService widgetDataService = new WidgetDataService()

    Widget getWidgetById(Long widgetId) {
        List<Widget> widgets = widgetDataService.getAllWidgets()
        widgets.each {
            if(it.id.equals(widgetId)) {
                return it
            }
        }

        return null
    }
}

然后在发生Groovy / Grails / closure魔术之后,我需要代码表现得好像我写的那样:

import groovy.util.logging.Slf4j
import org.apache.commons.lang3.exception.ExceptionUtils
import com.example.myapp.MyAppExceptionHandler

@Slf4j
class WidgetService {
    WidgetDataService widgetDataService = new WidgetDataService()

    MyAppExceptionHandler myAppExceptionHandler = new MyAppExceptionHandler()

    Widget getWidgetById(Long widgetId) {
        try {
            List<Widget> widgets = widgetDataService.getAllWidgets()
            widgets.each {
                if(it.id.equals(widgetId)) {
                    return it
                }
            }

            return null
        } catch(MyAppException maExc) {
            log.error(ExceptionUtils.getStackTrace(maExc))
            myAppExceptionHandler.handleOrRethrow(maExc)
        }
    }
}

关于如何实现这一目标的任何想法?我担心纯Groovy闭包可能会在运行时以某种方式干扰Grails对其服务的任何操作(因为它们都是不显式扩展父类的类)。

2 个答案:

答案 0 :(得分:2)

以下是我试图在评论中指出的内容:

package com.example

import groovy.util.logging.Log4j

@Log4j
trait SomeTrait {

    def withErrorHandler(Closure clos) {
        try {
            clos()
        } catch(Exception e) {
            log.error e.message
            throw new ApplicationSpecificException(
                "Application Specific Message: ${e.message}"
            )
        }
    }
}

服务类:

package com.example

class SampleService implements SomeTrait {

    def throwingException() {
        withErrorHandler {
            throw new Exception("I am an exception")
        }
    }

    def notThrowingException() {
        withErrorHandler {
            println "foo bar"
        }
    }
}

测试:

package com.example

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SampleService)
class SampleServiceSpec extends Specification {

    void "test something"() {
        when:
        service.throwingException()

        then:
        ApplicationSpecificException e = thrown(ApplicationSpecificException)
        e.message == "Application Specific Message: I am an exception"
    }

    void "test something again"() {
        when:
        service.notThrowingException()

        then:
        notThrown(Exception)
    }
}

这是sample app

Grails 3.0.9但是没关系。这适用于Grails 2.4。*

答案 1 :(得分:1)

您可以使用MetaInjection或Spring AOP拦截对Service类方法的调用。因此,您不必在每个Service类中编写闭包。您可以查看通过示例解释这两种方法的blog