Аспектная (-подобная) функция для замыканий

В 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

И это также записано в журнал.

Другие вопросы по тегам