Skip to content

Аспекты

Igor Polyakov edited this page Mar 17, 2018 · 4 revisions

Введение

В общем случае аспект состоит из двух частей: точки прикрепление (join point) и некоторого совета (advice), который должен быть применен в точке прикрепления. Применимо к библиотеке обе концепции отражаются в двух классах MethodBoundaryAspect и MethodInterceptAspect. Классы являются атрибутами, что позволяет отображать через них точки прикрепления и одновременно с этим предоставляют ряд методов для переопределения, в которых непосредственно реализуется логика совета.

Виды аспектов

  1. MethodBoundaryAspect. Выполняет переопределенные методы в точках перед и после фактического вызова основного метода. Развернутый вид метода, на который применен аспект данного вида можно представить следующим образом.
interface Foo 
{
  [MyAspect]
  void Bar();
}

// Вызов new FooImpl().Bar(); преобразуется в

try {
  MyAspect.OnEntry(pipeline);
  Bar();
  MyAspect.OnSuccess(pipeline);
} 
catch(Exception e) {
  MyAspect.OnException(pipeline);
} 
finally {
  MyAspect.OnExit(pipeline);
}

В случае, когда на метод применено несколько аспектов, в указанных точках срабатывает каждый из них. Сами аспекты упорядочиваются по свойству Order в порядке убывания. Если порядок не задан, то вызываются сначала аспекты верхнего уровня (примененные на интерфейс), а затем локальные аспекты на методах.

  1. MethodInterceptionAspect. Позволяет перехватывать вызов метода объекта. При этом вызов оригинального метода осуществляется посредством вызова метода Proceed() входного параметра IInvocation. Разрешено наличие только одного атрибута данного типа на методе.

Ограничения аспектов

  • Аспекты можно использовать только на интерфейсы и методы интерфейсов, на классах, свойствах, конструкторах и прочем - работать не будет;
  • Дублирующиеся аспекты удаляются. То есть, если на интерфейсе есть аспект [FooAspect] и на каком-то методе интерфейса есть такой же аспект, то будет примененен только аспект верхнего уровня;
  • С асинхронными методами нормально пока не работает. Доработки в планах;
  • Ответственность за исключения внутри точек внедрения в вызов оригинального метода на разработчике. Если произойдет исключение в каком-то из обработчиков (OnEntry, OnSuccess, etc) аспекта MethodBoundaryAspect , то вызов основного метода будет прерван, а возникшее исключение попадет в вызывающий код.
  • Аспект MethodInterceptionAspect может быть использован только на методах. При этом на один метод разрешено иметь только один аспект данного типа, однако это не ограничивает использование данного типа аспекта совместно с аспектами MethodBoundaryAspect.

Передача состояния между аспектами

Для передачи состояния между вызовами в рамках одного аспекта используйте свойство IInvocationPipeline.AspectExecutionState. Механизма для передачи состояния через весь пайплайн (между всеми аспектами) нет. Описанное выше свойство работает только на один аспект и другие аспекты изменений не увидят. Пример использования представлен тут.

Обработка исключений

  1. Если в процессе вызова исходного метода произошло исключение, то оно передается на вход аспекта в метод OnException. Если ни один аспект не установлен на метод - исключение с сохранением стека будет проброшено в вызывающий код.
  2. Аспект может скрыть исключение вызвав любую из версий pipeline.Return(). В этом случае исключение будет поглощено и клиентский код продолжит свою работу как ни в чем не бывало. Однако, если ожидается какой-то результат выполнения метода, то возврат таким образом может привести к возникновению NullReferenceException.
  3. Аспект может подменить исключение вызвав pipeline.Throw(Exception), в этом случае старое исключение будет затерто, а клиентский код получит новое исключение, установленное обработчиком.
  4. Если аспект не выполнит никаких действий по обработке исключений, то исключение будет передано на вход следующему аспекту. Если ни один из аспектов не остановил прокидывание исключения, то оно будет выброшено в клиентский код без потери стек-трейса.
  5. Если в процессе выполнения методов аспектов произойдет исключение, то оно будет выброшено во вне. Поэтому обработка исключений на уровне точек выполнения аспекта лежит на пользователях библиотеки.

Связывание объектов с аспектами

Наиболее прозрачным образом библиотека работает с паре с фреймворками по внедрению зависимостей, так как они берут работу по инъекции аспектов в исходные классы на себя. Впрочем, библиотеку можно использовать и без контейнеров (с некоторыми органичениями).

По умолчанию выполняется связывание, если на интерфейсе или на каком-либо его методе установлен атрибут, который наследуется от MethodAspect. Отключить применение аспектов на интерфейсе целиком или на отдельных методах можно, установив атрибут [SuppressWeaving]. Так же можно установить отдельный атрибут-маркер, по которому будет определяться необходимость применения аспектов на тип (см. секцию Конфигурация).