【问题标题】:How to wrap all Grails service methods with a Groovy closure?如何用 Groovy 闭包包装所有 Grails 服务方法?
【发布时间】:2023-03-08 06:09:01
【问题描述】:

这里是 Grails 2.4.x。

我要求我的所有 Grails 服务的所有方法(由 grails create-service <xyz> 生成)都使用以下逻辑“包装”/拦截:

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 的那个;和
  • myAppExceptionHandlercom.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 在运行时对其服务进行的任何操作(因为它们都是没有显式扩展父类的类)。

【问题讨论】:

  • 你问过a similar question a year ago。只需稍加调整即可使用相同的方法。通过将通用逻辑作为默认实现移动到特征,然后在服务类中实现特征。另一种方法是使用invokeMethod 拦截服务类中的每个方法并用try/catch 包装它们。使用这种方法的缺点是它必须在每个服务类中重复。我相信以前的方法会更干净,可以静态编译。
  • 谢谢@dmahapatro (+1) - 是的,我记得这一点,并且在问这个问题时确实想到了这一点,但就像我说的,我担心 Grails 可能会以某种方式干扰。如果您可以通过显示您建议的方法之一的代码示例将您的评论转移到答案中,我会很高兴为您提供绿色检查!

标签: grails groovy closures aop grails-services


【解决方案1】:

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

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。*

【讨论】:

  • 谢谢@dmahapatro (+1) - 只要允许我这样做,我就会奖励你绿色支票和赏金。
  • 还有@dmahapatro - 你知道withErrorHandler 返回的确切类型吗?空白?目的? def 总是因为某种原因让我害怕。谢谢!
  • 可以按照您想要的方式增强特征。它可以返回闭包实现的结果。在 Grails 3 中,我们可以更进一步,使用TraitInjector 优化上述逻辑,但以上对于 Grails 2.4 应该是好的。在 trait 中分配 def result = clos() 并返回结果。建议使用 def ,因为可以泛化默认 trait 实现。 def 是 Object 的同义词。 :) 它也会静态编译。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多