Диссертация (1148251), страница 13
Текст из файла (страница 13)
XML-КОНФИГУРИРОВАНИЕ АСПЕКТОВ В POSTSHARPЛогика действий аспекта содержится в переопределенных методах. Нижеприведен пример аспекта для поддержки транзакционности целевого метода [2]:[Serializable]//Укажем, что данный аспект должен быть вызван всегда после LogAspect (если он такжеприменяется)[AspectTypeDependency(AspectDependencyAction.Order,AspectDependencyPosition.After,typeof(LogAspect))]//Аспект перехватывает вызов целевого метода и наследует соотв. классpublic class RunInTransactionAspect : OnMethodBoundaryAspect {[NonSerialized]TransactionScope TransactionScope;//Действие аспекта выполнится перед целевым методомpublic override void OnEntry(MethodExecutionArgs args){this.TransactionScope=new TransactionScope(TransactionScopeOption.RequiresNew);}//Действие аспекта выполнится после успешного выполнения целевого методаpublic override void OnSuccess(MethodExecutionArgs args){this.TransactionScope.Complete();}//При возникновении исключения в целевом методе вызвать этот кодpublic override void OnException(MethodExecutionArgs args){//Аргумент типа MethodExecutionArgs предоставляет доступ к контексту точки внедренияargs.FlowBehavior = FlowBehavior.Continue;Transaction.Current.Rollback();Console.WriteLine("Transaction Was Unsuccessful!");}//Выполнить данное действие в любом случае после выполнения цлевого методаpublic override void OnExit(MethodExecutionArgs args){this.TransactionScope.Dispose();}}ЛИСТИНГ 17.
АСПЕКТ ДЛЯ ПОДДЕРЖКИ ТРАНЗАКЦИОННОСТИ ЦЕЛЕВОГО МЕТОДАК одной точке внедрения можно построить целую цепочку вызовов разныхаспектов, и PostSharp поддерживает возможность их упорядочения. Пусть,например, у нас определен простой аспект для протоколирования:68[Serializable]public class LogAspect : OnMethodBoundaryAspect {public override void OnEntry(MethodExecutionArgs args){Console.WriteLine("Entering [ {0} ] ...", args.Method);}//Разрешаем переадресовать вызов другим аспектам в цепочкеbase.OnEntry(args);public override void OnExit(MethodExecutionArgs args){Console.WriteLine("Leaving [ {0} ] ...", args.Method);}}//Разрешаем переадресовать вызов другим аспектам в цепочкеbase.OnExit(args);ЛИСТИНГ 18. АСПЕКТ ДЛЯ ПРОТОКОЛИРОВАНИЯТогда упомянутые аспекты можно применить к целевому методуследующим способом:[ExceptionAspect][LogAspect]static void Calc(){throw new DivideByZeroException("A Math Error Occurred...");}ЛИСТИНГ 19.
ПРИМЕНЕНИЕ АСПЕКТА В POSTSHARPКак и в AspectJ, к возможностям PostSharp также относится механизмдобавления к целевому классу новых полей, методов и реализации интерфейсов.Но несмотря на полную поддержку АОП, применение PostSharp не требуетспециальных навыков благодаря наличию готовых аспектов. РазработчикиPostSharp позиционируют его как расширение стандартного компиляторапаттернами (в виде аспектов) [80], которые после применения к целевому классуавтоматически реализуют следующую сквозную функциональность: уведомлениеоб изменении свойств (паттерн InotifyPropertyChanged [48]), функциональностьUndo/Redo значений свойств, проверку входных аргументов на корректныезначения (Code Contracts), протоколирование (logging), обеспечение потокобезопасности (thread safety) и обнаружение взаимных блокировок (deadlocks).692.5 Aspect.NETСледует отметить, что несмотря на свои достоинства, АОП не получилотакого же широкого признания, как другие технологии повышения качества кода:тестирование с помощью подставных (mock) объектов [32], контейнерыуправления зависимостями IoC (Microsoft Unity [132], Spring.NET [92]),рефакторинг [129] и т.п.
В настоящее время уже разработаны инструменты дляпромышленной АОП-разработки высокого качества, например, PostSharp дляплатформы Microsoft .NET и AspectJ для Java, однако если они и применяются, тодлядостаточноузкогокругаупомянутыхвышезадачсквознойфункциональности. По мнению автора, кроме необходимости изучения всемичленами команды нового подхода, его “ловушек” [3] и инструментов, этот фактобъясняется также такими факторами, как сильная зависимость развивающегосяпроекта от выбранного АОП-инструмента, а также дополнительными накладнымирасходами на вызовы аспектов. Предположим, какая-то команда решилаиспользоватькакой-либоАОП-инструмент.Проанализированысистемныетребования и сквозная функциональность выделена в аспекты.
После успешнойдемонстрации заказчику, тот решает увеличить команду для реализациидополнительных требований, перенести систему на новые устройства или воблачную инфраструктуру. Тогда возникают дополнительные проблемы, которыене присущи самой задаче: необходимость обучения новых сотрудниковпринципам выбранного АОП-инструмента, возможная неработоспособность этогоинструмента на новой платформе, дополнительные накладные расходы на вызовыаспектов в условиях ограниченной мощности мобильных устройств илиповышенные счета за потребление процессорного времени в облачнойинфраструктуре. Подобный сценарий может привести к полному отказу от АОПразработки и созданию следующей версии проекта традиционными способами.Кроме таких общих проблем, существуют ещё и частные проблемы вподходах конкретных инструментов к АОП.
Например, в PostSharp аспектыпривязываются к своим точкам внедрения с помощью пользовательских70атрибутов, которыми размечен исходный код проекта. Если необходима какая-тонастройка аспекта, это производится в атрибуте. Получается, что исходный кодцелевого приложения вынужден знать о наличии аспектов и регулировать ихработу. Отчасти проблему решают “групповые” (multicasting) атрибуты,которыми нужно пометить метаданные сборки.
Будучи собранными в одномместе целевого проекта, они определяют необходимые точки внедрения аспектов.Однако по-прежнему для их подключения требуется модификация целевого кода,что создает между ними двустороннюю зависимость. Это ведет к тому, что привозможном отказе от применения PostSharp, удалить все аспекты будетзатруднительно. В свою очередь, AspectJ имеет все возможности для АОПпрограммирования, но доступно исключительно для Java-платформы.Aspect.NET — это простой инструмент АОП для платформы .NET,требования и спецификации к которому были сформулированы профессором В.
О.Сафоновым в 2004 году [87]. При создании Aspect.NET поставлены и решеныследующие задачи:1) минимизировать время программиста на изучение системы;2) хранить код и правила внедрения аспектов отдельно от целевого проекта;3) дать возможность аспектам влиять на целевой код;4) снизить накладные расходы на вызов аспектов.Отметим, что возможность хранить код аспектов и правила их внедренияотдельно от целевого проекта дает возможность расширять его сквознойфункциональностью “бесшовно”, т.е. без модификации исходного кода. Еслицелевой проект на уровне исходного кода не имеет никаких зависимостей отаспектов и АОП-инструмента, то становится возможным применить АОПрефакторинг к унаследованному коду [72] (т.е.
повторяющиеся куски кода,пригодные для повторного использования, выносятся в отдельный проект исвязываются с целевым через средства АОП) или расширить его сквознойфункциональностью без какой бы то ни было модификации исходного кода илинастроек проекта. С учетом того, что компания Microsoft выпустила библиотекуEnterprise Library (EL) [64], которая позиционируется как набор готовых71функциональных блоков со сквозной функциональностью (Caching ApplicationBlock, Validation Application Block, Logging Application Block и т.п.), роль АОПинструмента можно упростить и свести к вставке вызовов соответствующихфункциональных блоков в нужных местах целевой программы.
Подробнеебесшовное применение Microsoft EL рассматривается в работе [114]. Например,PostSharp имеет такие исчерпывающие возможности по созданию аспектов, какперехватвызововметодов(MethodInterceptionAspect),доступкполю(LocationInterceptionAspect), инстанцирование аспектов во время компиляции(CompileTimeInitialize) и передача дополнительной информации при созданииаспекта в режиме выполнения (RuntimeInitialize). Однако в рамках простойпереадресации вызовов к сторонним службам, они приводят к усложнению кода идополнительным накладным расходам.
В тех случаях, когда перед вызовомцелевого достаточно вызвать статический метод аспекта, который вызоветстороннюю службу с нужными параметрами, PostSharp будет вынужденсериализовать (при компиляции) и десериализовать (во время выполнения)состояние аспекта из памяти для возможности его применения.Также упомянем проблему тестирования целевого кода, к которому на этапекомпиляции применяется аспект PostSharp. Хотя на уровне исходного кода бизнеслогика и аспект разделены по разным классам, содержатся они в одном проекте итакие популярные инструменты модульного тестирования как NUnit [47]применяют тесты к результирующей сборке, в которой целевой код и аспект ужесцеплены друг с другом. Это делает затруднительным тестирование самой бизнеслогики и приходится выключать PostSharp в свойствах целевого проекта на времяпрогона тестов.
При наличии сложной системы непрерывной интеграции(continuous integration) программистам, применяющим PostSharp, приходитсяискать нетривиальные решения [41]. В свою очередь, бесшовная интеграцияаспектов Aspect.NET лишена таких проблем: целевой проект ничего не знает проаспекты, его компиляция и тестирование проходит как обычно, а интеграция саспектами осуществляется во время компиляции аспектного проекта.72С помощью Aspect.NET можно определять аспекты в отдельных классах, азатем вплетать вызовы их методов в заданные места целевой сборки (joinpoints).Определения аспектов не зависят от конкретного языка, разрабатывать их можно влюбой среде разработки, поддерживающей платформу .NET.Aspect.NET вставляет действия на уровне MSIL-инструкций после этапакомпиляции целевой сборки (аналогично PostSharp), что влечет повышениепроизводительности целевого приложения по сравнению с IoC-контейнерами.Более того, такая “пост-обработка” дает возможность выбирать конкретные местаприменения действий аспектов.
В то время как IoC-контейнер перехватывает всевызовы целевого метода на этапе выполнения программы, а PostSharp предлагаетсформулировать фильтрующий метод, вызываемый во время компиляции, вAspect.NET применение аспектов происходит в два этапа: вначале в целевойсборке находятся все точки внедрения, удовлетворяющие правилам, затемпользователь в специальном окне имеет возможность отключить ненужные.Отфильтрованный список точек внедрения подается на этап внедрения аспектов,где инструментирование Microsoft IL-кода производится с помощью библиотекиMicrosoft Phoenix [65].
Стоит всё же отметить, что при необходимостипереконфигурирования аспектов прямо во время выполнения программы,альтернативы IoC-контейнерам нет.Аспектом может быть любой класс, производный от класса Aspect(предопределенного в библиотеке Aspect.NET) [132]. Реализация аспектаосуществляется статическими методами (“действиями”), которые затем будутвставлены компоновщиком в заданные точки внедрения (joinpoints) в сборкецелевого приложения.
Требуемое множество точек внедрения задается впользовательском атрибуте AspectAction() своего действия аспекта. Любоедействие можно вставлять перед (ключевое слово %before), после (%after) иливместо (%instead) вызова заданного целевого метода. Название целевого методазадается с помощью регулярных выражений относительно его сигнатуры. Такжевнутри действия аспекта можно использовать свойства базового класса Aspect,предоставляющие доступ к контексту точки внедрения [112]:731) SourceFileLine — это строка, представляющая номер строчки в исходномкоде файла, где расположен вызов целевого метода;2) SourceFilePath — это строка, представляющая путь к целевому файлуисходного кода, в котором расположен вызов целевого метода;3) TargetObject — это ссылка типа System.Object на объект в целевойпрограмме,ккоторомуприменяетсяцелевойметодвточкеприсоединения.