2015-10-30 11 views
1

Grails 2.4.x здесь.Как обернуть все методы обслуживания Grails с помощью Groovy закрытия?

У меня есть требование, что все методы всех моих 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/закрытие магия происходит мне нужен код ведут себя так, как будто я написал это нравится:

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 делает со своими сервисами под капотом во время выполнения (так как это все классы, которые явно не распространяют родительский класс).

+1

Вы задали [аналогичный вопрос год назад] (http://stackoverflow.com/questions/26407558/repetitive-try-catch-blocks-with-groovy-with-closure). Такой же подход можно использовать с небольшой настройкой. путем перемещения общей логики к признаку как реализации по умолчанию, а затем реализации признака в классах обслуживания. Другим подходом было бы использовать 'invokeMethod' для перехвата каждого метода в классе службы и обернуть их с помощью try/catch.Недостаток использования этого подхода заключается в том, что он должен повторяться в каждом классе обслуживания. Я считаю, что прежний подход будет более чистым и может быть статически скомпилирован. – dmahapatro

+0

Спасибо @dmahapatro (+1) - да, я помню это и на самом деле имел это в виду, спрашивая об этом, но, как я уже сказал, я беспокоился о том, что Грайль может каким-то образом вмешаться. Если вы можете переместить свой комментарий в ответ с примером кода, показывающим один из предложенных вами подходов, я с радостью дам вам зеленый чек! – smeeb

ответ

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

+0

Спасибо @dmahapatro (+1) - Я дам вам зеленый чек и награду, как только SO разрешит мне это сделать. – smeeb

+0

Также @dmahapatro - знаете ли вы, какой именно тип 'withErrorHandler' возвращается? Пустота? Объект? 'def' всегда пугает меня по какой-то причине. Благодаря! – smeeb

+0

Тит может быть улучшен так, как вы хотите. Он может вернуть результат реализации закрытия. В Grails 3 мы можем сделать еще один шаг и оптимизировать вышеуказанную логику с помощью 'TraitInjector', но выше должно быть хорошо для Grails 2.4. Назначьте 'def result = clos()' в результате и результат возврата. Использование def рекомендуется, так как реализация по умолчанию может быть обобщена. def является синонимом Object. :) Он также будет статически компилироваться. – dmahapatro

1

Вы можете перехватить вызовы методам класса обслуживания либо с помощью MetaInjection, либо Spring AOP. Поэтому вам не нужно писать закрытие в каждом классе службы. Вы можете посмотреть на этот blog, который объясняет оба подхода с примерами.

 Смежные вопросы

  • Нет связанных вопросов^_^