Как обернуть все методы обслуживания Grails замыканием Groovy?
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
; а также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 ответа
Вот что я пытался указать в своем комментарии:
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)
}
}
Вот пример приложения.
Grails 3.0.9 но это не должно иметь значения. это применимо для Grails 2.4.*
Вы можете перехватывать вызовы методов класса Service, используя MetaInjection или Spring AOP. Таким образом, вам не нужно писать замыкание в каждом классе Service. Вы можете заглянуть в этот блог, который объясняет оба подхода с примерами.