-
Notifications
You must be signed in to change notification settings - Fork 0
Аспекты
В общем случае аспект состоит из двух частей: точки прикрепление (join point) и некоторого совета (advice), который должен быть применен в точке прикрепления.
Применимо к библиотеке обе концепции отражаются в двух классах MethodBoundaryAspect и MethodInterceptAspect. Классы являются атрибутами, что позволяет отображать через них точки прикрепления и одновременно с этим предоставляют ряд методов для переопределения, в которых непосредственно реализуется логика совета.
- 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 в порядке убывания. Если порядок не задан, то вызываются сначала аспекты верхнего уровня (примененные на интерфейс), а затем локальные аспекты на методах.
-
MethodInterceptionAspect. Позволяет перехватывать вызов метода объекта. При этом вызов оригинального метода осуществляется посредством вызова метода
Proceed()входного параметраIInvocation. Разрешено наличие только одного атрибута данного типа на методе.
- Аспекты можно использовать только на интерфейсы и методы интерфейсов, на классах, свойствах, конструкторах и прочем - работать не будет;
- Дублирующиеся аспекты удаляются. То есть, если на интерфейсе есть аспект
[FooAspect]и на каком-то методе интерфейса есть такой же аспект, то будет примененен только аспект верхнего уровня; - С асинхронными методами нормально пока не работает. Доработки в планах;
- Ответственность за исключения внутри точек внедрения в вызов оригинального метода на разработчике. Если произойдет исключение в каком-то из обработчиков (OnEntry, OnSuccess, etc) аспекта
MethodBoundaryAspect, то вызов основного метода будет прерван, а возникшее исключение попадет в вызывающий код. - Аспект
MethodInterceptionAspectможет быть использован только на методах. При этом на один метод разрешено иметь только один аспект данного типа, однако это не ограничивает использование данного типа аспекта совместно с аспектамиMethodBoundaryAspect.
Для передачи состояния между вызовами в рамках одного аспекта используйте свойство IInvocationPipeline.AspectExecutionState. Механизма для передачи состояния через весь пайплайн (между всеми аспектами) нет. Описанное выше свойство работает только на один аспект и другие аспекты изменений не увидят. Пример использования представлен тут.
- Если в процессе вызова исходного метода произошло исключение, то оно передается на вход аспекта в метод
OnException. Если ни один аспект не установлен на метод - исключение с сохранением стека будет проброшено в вызывающий код. - Аспект может скрыть исключение вызвав любую из версий
pipeline.Return(). В этом случае исключение будет поглощено и клиентский код продолжит свою работу как ни в чем не бывало. Однако, если ожидается какой-то результат выполнения метода, то возврат таким образом может привести к возникновениюNullReferenceException. - Аспект может подменить исключение вызвав
pipeline.Throw(Exception), в этом случае старое исключение будет затерто, а клиентский код получит новое исключение, установленное обработчиком. - Если аспект не выполнит никаких действий по обработке исключений, то исключение будет передано на вход следующему аспекту. Если ни один из аспектов не остановил прокидывание исключения, то оно будет выброшено в клиентский код без потери стек-трейса.
- Если в процессе выполнения методов аспектов произойдет исключение, то оно будет выброшено во вне. Поэтому обработка исключений на уровне точек выполнения аспекта лежит на пользователях библиотеки.
Наиболее прозрачным образом библиотека работает с паре с фреймворками по внедрению зависимостей, так как они берут работу по инъекции аспектов в исходные классы на себя. Впрочем, библиотеку можно использовать и без контейнеров (с некоторыми органичениями).
По умолчанию выполняется связывание, если на интерфейсе или на каком-либо его методе установлен атрибут, который наследуется от MethodAspect. Отключить применение аспектов на интерфейсе целиком или на отдельных методах можно, установив атрибут [SuppressWeaving]. Так же можно установить отдельный атрибут-маркер, по которому будет определяться необходимость применения аспектов на тип (см. секцию Конфигурация).