如何用 Groovy 闭包包装所有 Grails 服务方法?
How to wrap all Grails service methods with a Groovy closure?
Grails 2.4.x 这里.
我要求由 grails create-service <xyz>
生成的我所有 Grails 服务的所有方法 "wrapped"/按照以下逻辑拦截:
try {
executeTheMethod()
} catch(MyAppException maExc) {
log.error(ExceptionUtils.getStackTrace(maExc))
myAppExceptionHandler.handleOrRethrow(maExc)
}
其中:
log.error(...)
是当您使用 @Slf4j
注释注释 class 时获得的 SLF4J 提供的记录器;和
ExceptionUtils
是来自 org.apache.commons:commons-lang3:3.4
的那个;和
myAppExceptionHandler
是 com.example.myapp.MyAppExceptionHandler
类型;和
- Grails 服务中定义的每个方法都存在这种行为(或者在需要以某种方式显式调用它的情况下可以选择存在)
很明显,这个包装器代码还需要为那些 class 包含 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 在运行时对其服务所做的任何事情(因为它们都是 class 没有显式扩展父 class).
您可以使用 MetaInjection 或 Spring AOP 拦截对服务 class 方法的调用。所以你不必在每个服务中都写闭包class。您可以查看此 blog,它通过示例解释了这两种方法。
这是我在评论中试图指出的内容:
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}"
)
}
}
}
服务class:
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.*
Grails 2.4.x 这里.
我要求由 grails create-service <xyz>
生成的我所有 Grails 服务的所有方法 "wrapped"/按照以下逻辑拦截:
try {
executeTheMethod()
} catch(MyAppException maExc) {
log.error(ExceptionUtils.getStackTrace(maExc))
myAppExceptionHandler.handleOrRethrow(maExc)
}
其中:
log.error(...)
是当您使用@Slf4j
注释注释 class 时获得的 SLF4J 提供的记录器;和ExceptionUtils
是来自org.apache.commons:commons-lang3:3.4
的那个;和myAppExceptionHandler
是com.example.myapp.MyAppExceptionHandler
类型;和- Grails 服务中定义的每个方法都存在这种行为(或者在需要以某种方式显式调用它的情况下可以选择存在)
很明显,这个包装器代码还需要为那些 class 包含 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 在运行时对其服务所做的任何事情(因为它们都是 class 没有显式扩展父 class).
您可以使用 MetaInjection 或 Spring AOP 拦截对服务 class 方法的调用。所以你不必在每个服务中都写闭包class。您可以查看此 blog,它通过示例解释了这两种方法。
这是我在评论中试图指出的内容:
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}"
)
}
}
}
服务class:
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.*