Аспектная (-подобная) функция для замыканий
В Grails (по крайней мере до текущей версии 2.2) теги-теги являются замыканиями. Для определенных замыканий taglib я бы хотел использовать совет типа "вокруг" / завернуть замыкание в перехватчик.
Другими словами, скажем, есть эта taglib прямо из документа Grails:
class SimpleTagLib {
def emoticon = { attrs, body ->
out << body() << (attrs.happy == 'true' ? " :-)" : " :-(")
}
}
Теперь, не изменяя код taglib, я хотел бы узнать, сколько времени займет выполнение "смайлика".
Spring AOP (и все остальные AOP, которые я могу найти), похоже, работает только на Java-методах, а библиотеки тегов всегда основаны на замыканиях. Для этого идеально подойдет точка "вокруг", но я не могу понять, как заставить это работать.
1 ответ
Я написал нечто похожее, что я поставил как публичное закрытие в категории, которую я затем смешал с сервисами:
// TimingCategory.groovy
/**
* Provides common AOP timing functionality to Services which mixin this category.
*/
class TimingCategory {
static Closure timer = { String label = "The call", Closure closure ->
Long start = System.currentTimeMillis()
def result = closure.call()
Long end = System.currentTimeMillis()
Long duration = end - start
log.warn "${label} took ${duration} ms"
return result
}
}
В других классах вы просто ссылаетесь на timer
закрытие как таковое:
@Mixin(TimingCategory)
public class WhateverService {
public String doSomeWork() {
timer "Doing a lot of work", {
1000.times { doSomething() }
someMethodWithAStringReturnValue()
}
}
}
Это даст вам вывод журнала "WARN: выполнение большой работы заняло nn ms" и вернет значение внутреннего замыкания в качестве возвращаемого значения doSomeWork
метод.
Для вашего экземпляра taglib просто оберните out << ...
в
timer "Writing an emoticon", {
// your code
}
код.
Если вам не нужно проходить через внутреннее возвращаемое значение, вы можете вместо этого вернуть продолжительность в результате вызова замыкания.
Обновление:
Возможно, я неправильно прочитал - вы спрашиваете, как обернуть выполнение taglib, не изменяя код taglib вообще? Как насчет создания пользовательского taglib, который принимает тело и передает его другим taglibs для выполнения?
Я не пробовал это, но что-то вроде:
class TimedTagLib {
static namespace = "timer"
def timedTag = { attrs, body ->
timer "Executing the timed tag", {
out << body()
}
}
}
И вызывая это как
<timer:timedTag><g:emoticon whatever="something">Text</g:emoticon></timer:timedTag>
Обновление 2:
Итак, я попробовал это. Работает отлично. Мой окончательный код (я добавил второе закрытие таймера, которое возвращает продолжительность):
// TimedTagLib.groovy
@Mixin(TimingCategory)
class TimedTagLib {
static namespace = "timer"
def timedTag = { attrs, body ->
def duration = returnTimer "Printing the timed tag", {
out << body()
}
out << "Took ${duration} ms to print"
}
}
И мнение:
// someView.gsp
<timer:timedTag>
<g:formatLocalDate date="${LocalDate.now()}" />
</timer:timedTag>
В результате HTML выглядит так:
03/19/2013
Took 6 ms to print
И это также записано в журнал.