Диссертация (1148251), страница 16
Текст из файла (страница 16)
ОБРАБОТЧИК ЩЕЛЧКА МЫШИ ПО КНОПКЕ СТРАНИЦЫПри АОП-разработке может потребоваться перехватить вызов данногообработчика для добавления текста в этот элемент. Метод LogButton_Clickвызывается средой ASP.NET и у компоновщика нет доступа к вызывающемукоду, чтобы обернуть его вызовом действий аспекта. В этом случае PostSharpпредлагает применять правило внедрения OnMethodBoundaryAspect, при которомдействия аспекта вставляются внутри тела целевого метода. Однако, еслипотребуется получить доступ к защищенным полям или методам целевого класса,придется использовать рефлексию .NET.
Более оптимальное решение здесь —применить паттерн “Декоратор” [111] и создать наследника класса Default спереопределенным целевым методом LogButton_Click [114]:[ReplaceBaseClass]public class AspectClass : Default {protected void LogButton_Click(object sender, EventArgs e) {//Совершаем действия перед вызовом целевого метода…base.LogButton_Click(sender, e);//Совершаем действия после вызова целевого методаTextControl.Text += ” But AOP is watching on you”;}}ЛИСТИНГ 27. АСПЕКТНЫЙ КЛАСС-НАСЛЕДНИКСпециальный пользовательский атрибут ReplaceBaseClass предписываеткомпоновщикуAspect.NETзаменитьцелевойкласссвоимаспектным86наследником.
Для этой цели автором разработан следующий алгоритм,реализованный в компоновщике аспектов.1. Заменить в исходной сборке все вызовы методов базового целевогокласса (в том числе и конструкторы) на вызовы методов его наследника васпектной сборке.2. Принудительно объявить виртуальными те методы целевого класса,которые переопределены в замещающем его наследнике. Если онизакрыты (private), то сделать их защищенными (protected).3.
Если вызов этих методов в исходной сборке производится с помощьюMSIL-инструкции call или ldftn, заменить их на callvirt и ldvirtftnсоответственно.4. Объединить с помощью инструмента ILRepack [69] (из проектаMono.Cecil) аспектную сборку и целевую.5. Присвоить какое-нибудь служебное имя базовому целевому классу, а егопервоначальное имя — замещающему наследнику из аспекта.Подобное создание декораторов (но во время выполнения программы)лежит в основе IoC-контейнеров. Преимуществами такого подхода являетсяпростота подмены классов для пользователя, а также использование толькоштатного синтаксиса языка .NET. Теперь с помощью аспекта можно: уточнятьповедение любого метода целевого класса, реализовывать в нем дополнительныеинтерфейсы, накладывать различные пользовательские атрибуты и т.п. Посравнению с обобщенным введением интерфейса в PostSharp мы теряемвозможность декларативно вводить интерфейсы в целевые классы, однакополучаем возможность использовать их члены в аспекте.
Вычислительнаясложность такого алгоритма O(N), где N — количество MSIL-инструкций висходной сборке. Поскольку в компоновщике аспектов Aspect.NET эти операцииобъединены с операциями вставки аспектов (также сложностью O(N)), общаяасимптотическая сложность компоновки аспектов не увеличилась.87Следует отметить, что данное решение всё же нетипично для АОП, гдеаспект описывается максимально изолированно от целевого класса, в то время какнаследование предполагает сильную связь. В то же время, выразительныесредства АОП решают ограниченный круг задач и, если есть возможность решитьзадачу более элегантным способом, стоит им воспользоваться. Косвенно этоподтверждается действиями команды PostSharp, которая взяла курс на созданиеинструментов(PostSharpToolkits[79]),предназначенныхдлярешенияспецифичных задач (напр.
многопоточности и диагностики). Применение для техже целей исключительно аспектов PostSharp вызывает практические затруднения.3.4 Внедрение в конфигурационный файл веб-приложения настроекаспектовБесшовное расширение бизнес-логики нефункциональными требованиями спомощью сторонних библиотек (напр. Microsoft EL) заключается в созданииотдельного проекта в виде библиотеки классов (dll), добавлению к немуаспектных классов, а также интеграции в него сторонней библиотеки черезменеджера зависимостей Nuget. В процессе интеграции в зависимости аспектногопроекта будут добавлены необходимые ссылки и, возможно, будет созданконфигурационный XML-файл с настройками сторонней библиотеки.
Послеслияния компоновщиком Aspect.NET аспектной сборки и целевой, объектысторонней библиотеки будут обращаться к конфигурационному файлу целевогопроекта, в котором отсутствуют необходимые настройки. Пользователюприходитсявноситьизменениявцелевойконфигурационныйфайлсамостоятельно, что затрудняет его редактирование, ведь теперь все возможныенастройки всех аспектов и целевого проекта содержатся в одном файле. Особеннотакая ситуация критична в веб-приложениях, поскольку их файл web.configсодержит достаточно много узлов и назвать “бесшовным” такое применениеаспектов нельзя.88Microsoft Visual Studio предлагает свою технологию по трансформацииweb.config файла при сборке или публикации проекта [28].
В обозревателепроекта (solution explorer) можно раскрыть узел web.config и получить доступ кфайлам web.Debug.config и web.Release.config. В них с помощью XML-атрибутовxdt:Transform и xdt:Locate задается трансформация web.config при сборке врежиме Debug или Release. Но в нашем случае необходимо трансформироватьцелевой web.config только при внедрении соответствующего аспекта и оставитьего неизменным при запуске целевого проекта без аспектов.
К тому же,расстановка XML-атрибутов вручную может привести к ошибкам.Итак, сформулируем проблему по бесшовному слиянию целевой иаспектной конфигурации. Есть два проекта, в каждом из них есть свойконфигурационный файл web.config. Необходимо предложить решение, котороебы автоматически обеспечивало слияние аспектных настроек с целевыми, но приэтом сами файлы с настройками оставались бы неизменными.
Сокращенныепримеры исходных и результирующего файла приведены ниже (Листинг 28.Аспектный web.config, Листинг 29. web.config, Листинг 30. Результирующийweb.config).<?xml version="1.0" encoding="utf-8" ?><configuration><configSections><sectionname="loggingConfiguration"type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings,Microsoft.Practices.EnterpriseLibrary.Logging,Version=6.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" requirePermission="true" /></configSections><loggingConfiguration name="" tracingEnabled="true" defaultCategory="General"><listeners><addname="DatabaseTraceListener"type="Microsoft.Practices.EnterpriseLibrary.Logging.Database.FormattedDatabaseTraceListener,Microsoft.Practices.EnterpriseLibrary.Logging.Database,Version=6.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35"listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Database.Configuration.FormattedDatabaseTraceListenerData,Microsoft.Practices.EnterpriseLibrary.Logging.Database,Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"databaseInstanceName="Logging" writeLogStoredProcName="WriteLog"addCategoryStoredProcName="AddCategory" formatter="Text Formatter" /></listeners></loggingConfiguration></configuration>ЛИСТИНГ 28.
АСПЕКТНЫЙ WEB.CONFIG89<?xml version="1.0" encoding="utf-8"?><configuration><configSections><sectionname="entityFramework"type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,EntityFramework,Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/></configSections><location path="admin"><system.web><authorization><deny users="?" /></authorization></system.web></location></configuration>ЛИСТИНГ 29. WEB.CONFIG ЦЕЛЕВОГО ПРОЕКТА<?xml version="1.0" encoding="utf-8"?><configuration><configSections><sectionname="entityFramework"type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,EntityFramework,Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/><sectionname="loggingConfiguration"type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings,Microsoft.Practices.EnterpriseLibrary.Logging,Version=6.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" requirePermission="true" /></configSections><location path="admin"><system.web><authorization><deny users="?" /></authorization></system.web></location><loggingConfiguration name="" tracingEnabled="true" defaultCategory="General"><listeners><addname="DatabaseTraceListener"type="Microsoft.Practices.EnterpriseLibrary.Logging.Database.FormattedDatabaseTraceListener,Microsoft.Practices.EnterpriseLibrary.Logging.Database,Version=6.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35"listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Database.Configuration.FormattedDatabaseTraceListenerData,Microsoft.Practices.EnterpriseLibrary.Logging.Database,Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"databaseInstanceName="Logging" writeLogStoredProcName="WriteLog"addCategoryStoredProcName="AddCategory" formatter="Text Formatter" /></listeners></loggingConfiguration></configuration>ЛИСТИНГ 30.