Диссертация (1148251), страница 11
Текст из файла (страница 11)
АСПЕКТ, СВЯЗЫВАЮЩИЙ ТОЧКУ И ЕЁ ТЕНЬВ данном примере присутствуют основные АОП возможности AspectJ, втом числе механизм добавления (private introduction) нового члена shadow вцелевой класс Point, работа с этим членом внутри аспекта, а также описаниенаборов точек внедрения вместе с захватом аргументов и контекста целевогометода.Наконец, продемонстрирован метод доступа getShadowCount() к состояниюаспекта. В AspectJ есть три способа ассоциации объекта аспекта с целевымобъектом: один общий разделяемый объект на все целевые объекты (per JVM),56один аспектный объект на один целевой объект (per object) и один общийаспектный объект для стека вызовов набора точек внедрения (per control flow).
Сучетом того, что состояние shadowCount принадлежит конкретному объектуаспекта, доступ к нему организуется через служебный метод aspectOf().Остальные возможности языка AspectJ приведены в [96].Стоит также отметить, что в AspectJ используются три модели компоновкиаспектов с целевым кодом.1. Во время компиляции (compile-time weaving). Специальный компиляторajc обрабатывает исходные тексты аспектов и целевого проекта, а затемкомпилирует их в одну сборку [45].2.
Пост-компиляция (post-compile weaving). Вначале целевой и аспектныеисходные тексты компилируются штатным javac компилятором, а затемajc сливает их на уровне байт-кода. Именно такой способ обеспечиваетбесшовную интеграцию аспектов [45].3. Во время загрузки классов в JVM (load-time weaving).
Вызовы действийаспектов встраиваются в байт-код целевых классов при их загрузкев JVM.Подводя итог, можно сказать, что AspectJ обладает всеми АОП-функциямидля вынесения сквозной функциональности в аспекты на платформе Java.Большинство АОП-инструментов могут только лишь приблизиться к еговозможностям. Однако далеко не все эти возможности необходимы дляуспешногоАОП-программированиявоблачнойсреде.Большоечисловозможностей ведет к повышению сложности для программиста и до сих пор всвоей повседневной работе AspectJ используется лишь небольшой долейпрограммистов.Таким образом, при реализации нового АОП-инструмента(например, для платформы .NET), стоит сконцентрироваться на его упрощении. Вразделе 5 данной главы будет рассмотрено сравнение Aspect.NET с AspectJ.572.2 Бесшовное аспектно-ориентированное программирование вебсервисов в FuseJВеб-сервисы—этосамодостаточныепрограммныекомпоненты,сконцентрированные на решении одной задачи и доступные через Интернет иливиртуальную сеть.Каждый веб-сервис объявляет открытый интерфейс, через который с ниммогут работать системы, запущенные на различном оборудовании илиоперационных системах.
Стандартизация протокола доступа к веб-сервисампозволила создавать слабосвязанные и кроссплатформенные приложения. Такойподход называется компонентно-ориентированной разработкой (Component-BasedSoftware Development, CBSD) [95]. Несмотря на то, что его применение позволяетинкапсулировать различную функциональность в одном модуле, в случае“сквозной” функциональности вызовы этого модуля будут распределены поцелевому коду. Авторы FuseJ предлагают объединить принципы АОП и CBSD водном фреймворке для платформы Java, позволяющем бесшовно составитьприложение из веб-сервисов и обеспечивающим возможность стыковать ихаспектно-ориентированныеобразом[94].Бесшовностьвданномслучаезаключается в том, что программные компоненты разрабатываются как обычныеклассы, без учета того, будут ли они использованы аспектно-ориентированнымобразом.Конструирование веб-сервисов в FuseJ основано на описании интерфейсов.Каждый веб-сервис (service) в FuseJ должен декларировать как свой интерфейс,который предоставляется его клиентам (provides), так и ожидаемый интерфейс(expects) другого веб-сервиса, который будет необходим для реализации.
Именнос помощью ожидаемого интерфейса организуется взаимодействие между вебсервисами:58interface BookHotel {boolean bookHotel(String hotelName, int numberOfNights);int computeHotelPrice(String hotelName, int numberOfNights);}interface ChargeCustomer {void chargeAmount(int amount);}service BookingService {provides BookHotel;expects ChargeCustomer;}ЛИСТИНГ 6. ИНТЕРФЕЙСЫ ВЕБ-СЕРВИСОВЗатем создается класс, реализующий требуемый веб-сервис:class BookingComponent implements BookingService{HotelPriceDb hotel_database = new HotelPriceDb();public boolean bookHotel(String hotelName, int numberOfNights){//Здесь должен быть управляющий код...//Считаем цену для этого отеляint price = computeHotelPrice(hotelName, numberOfNights);chargeAmount(price);return true;}public int computeHotelPrice(String hotelName, int numberOfNights){int rate = hotel_database.getPrice(hotelName);return rate * numberOfNights;}}ЛИСТИНГ 7.
РЕАЛИЗАЦИЯ BOOKINGSERVICEПодстановка нужных объектов вместо ожидаемых интерфейсов происходитво время выполнения программы, однако в отличие от них соответствиеопределяется не в целевом коде, а в отдельном “коннекторе”:interface CreditCardPayement {int chargeCreditCard(int amount, String cardNumber);}connector BookingPayment{connect:CreditCardPayment.chargeCreditCard(int amount, String cardNumber);for:BookingComponent.BookingService.ChargeCustomer.chargeAmount(int amount);where:cardnumber = "012-34567-89";}ЛИСТИНГ 8.
«КОННЕКТОР» ДЛЯ BOOKINGСогласно приведенному описанию, фреймворк FuseJ во время выполненияпрограммы перехватит все вызовы исходного (source) метода (задаваемого вблоке for), подставит вместо них целевой (target) метод (определяемый в блокеconnect). В том случае, когда их сигнатуры не совпадают, FuseJ сопоставляетаргументы с одинаковым порядковым номером и типом, а остальные определяет59из блока where. Дополнительно в блоке when можно использовать булевскиефункции Java для указания динамических условий, при которых будетпроизведена замена.Аспектно-ориентированные возможности по вызову исходного методавместо, перед или после целевого реализуются специальными конструкциями вконнекторах:interface Discount {int applyDiscount(int price, int percent);}class DiscountComponent implements Discount {}int applyDiscount(int price, int percent) {return (price - ((price*percent) / 100));}ЛИСТИНГ 9.
ИНТЕРФЕЙС DISCOUNT И КЛАСС, РЕАЛИЗУЮЩИЙ ЕГОconnector BookingDiscount {}connect:DiscountComponent.Discount.applyDiscount(int price, int percent);around returning :BookingComponent.BookingService.BookHotel.computeHotelPrice(String,returns(int price);where:percent = 10;when:TimeComponent.Timing.isChristmas();int)&&ЛИСТИНГ 10. «КОННЕКТОР», УПРАВЛЯЮЩИЙ ВЫЗОВОМ ЦЕЛЕВОГО МЕТОДАНапример, вышеуказанный коннектор декларирует фреймворку FuseJзаменить возврат результата в исходном методе на вызов целевого (который вэтом случае аналогичен действию аспекта) только в том случае, когда в этотмомент выполняется условие TimeComponent.Timing.isChristmas().Специфицированные таким образом сервисы и коннекторы предписываютсоздать веб-сервисы, которые можно использовать как ожидаемые при созданиидругих,обеспечиваяповторноеиспользованиевспецификацияхFuseJ.Связывание различных сервисов в рамках коннекторов позволяет быстрозаменять одни реализации другими, однако преимущества данного подходавозникают только при программировании веб-сервисов, так как результирующиепосле FuseJ классы нельзя явно вызывать из Java-кода, например, для создания60библиотеки классов.
Подход FuseJ имеет сходные черты с IoC-контейнеромSpring, в обоих случаях сопоставление интерфейсов и реализаций описываетсябесшовным образом в отдельных сущностях — коннекторах FuseJ или XMLфайлах Spring. Преимуществом FuseJ является возможность декларативнокомбинировать одни веб-сервисы для создания других. По сравнению с аспектамив AspectJ и Aspect.NET, FuseJ отделяет реализацию аспекта (класс веб-сервиса) отправил его внедрения (коннектор). Таким образом, аспект не имеет никакихсведений о том, в какой точке внедрения он будет выполняться и поменять еёповедение он может только через ожидаемый интерфейс. Это повышаетсвязанность аспекта и предсказуемость его действий. Тем не менее, FuseJ можетперехватывать только открытые методы, а на целевой проект накладываютсядополнительные требования: его разработка должна производиться на основеинтерфейсов, что затрудняет бесшовное применение аспектов к унаследованномукоду.
Также по сравнению со статическим применением аспектов, использованиеFuseJ приводит к дополнительным накладным расходам.2.3 Применение контейнера управления зависимостями Microsoft Unityдля АОП на платформе .NETПаттерн управления зависимостями (Inversion of control, IoC) призванпереложить ответственность за создание объектов на специальный контейнер.Если программа изначально спроектирована с выделением интерфейсов для всехопераций, которыев дальнейшем могут эволюционировать, топрямоеинстанцирование конкретных классов (через оператор new) приводит к жесткойсвязности между различными компонентами.
Когда приходит время подставитьссылку на другой класс, реализующий тот же интерфейс, то приходится вручнуюнаходить все соответствующие места в коде и менять вызовы конструкторов. Всёэто нарушает принцип проектирования “открыт для расширения, закрыт длямодификации” OCP [59] и выходом будет разрыв зависимости междуинтерфейсомиегореализациейнекойфабрикой,контейнером.Набор61соответствий между интерфейсами и реализациями может быть задан как в коде,так и в XML-файле, что позволяет расширять приложение, не затрагиваясуществующие компоненты. Большинство IoC-контейнеров позволяет такжесопоставлять интерфейсы и объекты, созданные вне контейнера. Более того, еслиIoC-контейнер будет возвращать ссылку не на объект целевого типа, а на проксиобъект, инкапсулирующий в себе данный объект, то появляется возможностьперехвата вызовов к методам целевого класса по паттерну “Перехватчик”(Interceptor) [49].
Такая возможность перехвата вызовов представляет собойпростейшую АОП-функциональность.Ввиду актуальности данной проблемы, в настоящее время разработаномножество IoC-контейнеров для платформы Microsoft .NET (Castle Windsor [128],Autofac [104], NInject [16], Spring.NET и т.д.). Компания Microsoft такжепредлагает свое решение Unity, которое являетсякода одним из функциональныхблоков Microsoft Enterprise Library. Несмотря на разные способы управлениязависимостями, все IoC-контейнеры служат одной цели и, без ущерба дляпонимания, можно рассмотреть лишь контейнер Unity [30].Подробный пример перехвата вызовов к целевому объекту с помощью Unity2.0 приведен в статье [15]. Предположим, что требуется реализовать простойпрокси-сервер.