Главная » Spring » Внедрение новых возможностей с помощью аспектов Spring

0

В некоторых языках программирования, таких как Ruby и Groovy, есть понятие открытых классов. Они позволяют добавлять в объ- екты или классы новые методы без непосредственного изменения определения этих объектов/классов. К сожалению, язык Java не обладает такими динамическими возможностями. Как только класс будет скомпилирован, у вас остается совсем немного возможностей расширить его функциональные возможности.

Но если задуматься, разве аспекты, рассматривающиеся в этой главе, не являются такой возможностью? Конечно, мы не добавляли новых методов в объекты, но мы расширяли функциональные воз- можности уже существующих методов. Коль скоро аспекты позво- ляют обертывать существующие методы новыми функциональными возможностями, почему бы с их помощью не попробовать добавлять новые методы? В действительности концепция внедрения, сущест- вующая в аспектно-ориентированном программировании, позволяет присоединять новые методы к компонентам Spring.

Напомню, что аспекты в Spring – это всего лишь промежуточ- ные объекты-обертки (прокси-объекты), реализующие те же самые интерфейсы, что и компоненты, которые они обертывают. Что, ес- ли кроме реализации этих интерфейсов прокси-объект будет со- держать реализацию некоторого другого интерфейса? Тогда любой

компонент, окруженный таким аспектом, будет выглядеть как объ- ект, реализующий дополнительный интерфейс, даже если лежащий в его основе класс в действительности не реализует его. На рис. 5.7 изображено, как действует этот механизм.

Рис. 5.7. С помощью Spring AOP можно внедрять новые методы в компоненты.

Прокси-объект перехватывает вызовы и делегирует их другому объекту, реализующему новый метод

На рис. 5.7 видно, что при вызове метода внедренного интерфейса прокси-объект делегирует вызов некоторому другому объекту, со- держащему реализацию нового интерфейса. Фактически это позво- ляет разбить реализацию компонента на несколько классов.

Попробуем воплотить эту идею на практике. Представим, что нам потребовалось внедрить следующий интерфейс Contestant во все компоненты, представляющие участников конкурса в примере:

package com.springinaction.springidol; public interface Contestant {

void   receiveAward();

}

Я понимаю, что можно было бы обойти все реализации интер- фейса Performer и изменить их, добавив реализацию интерфейса Contestant (соперник). Но в общем и целом это может быть не самый благоразумный шаг (потому что Contestant (соперник) и Performer (исполнитель) – необязательно взаимоисключающие понятия). Бо-

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

К счастью, возможность внедрения, поддерживаемая AOP, в со- стоянии помочь решить эту проблему без изменения архитектурных решений и не требуя изменять существующие реализации. Для осу- ществления описанной задачи необходимо задействовать элемент

<aop:declare-parents>:

<aop:aspect>

<aop:declare-parents

types-matching="com.springinaction.springidol.Performer+" implement-interface="com.springinaction.springidol.Contestant" default-impl="com.springinaction.springidol.GraciousContestant"

/>

</aop:aspect>

Как следует из его имени, элемент <aop:declare-parents> объявляет, что компоненты, к которым применяется описываемый аспект, при- обретают новых родителей в иерархии наследования. В частности, в данном случае утверждается, что компоненты, чьи типы совмести- мы с интерфейсом Performer (определяется атрибутом types-matching), получают в иерархии интерфейс Contestant (определяется атрибутом implement-interface) в иерархии наследования. Последний атрибут элемента описывает, где находятся реализации методов интерфейса Contestant.

Существуют два способа определения реализации внедренного интерфейса. В данном случае был использован атрибут default-impl, явно определяющий реализацию посредством полного имени класса. Другой способ заключается в использовании атрибута delegate-ref:

<aop:declare-parents

types-matching="com.springinaction.springidol.Performer+" implement-interface="com.springinaction.springidol.Contestant" delegate-ref="contestantDelegate"

/>

Атрибут delegate-ref ссылается на компонент Spring, играющий роль внедренного делегата. Он предполагает существование компо- нента с идентификатором contestantDelegate в контексте Spring:

<bean id="contestantDelegate" class="com.springinaction.springidol.GraciousContestant"    />

Разница между двумя способами определения делегата с помощью атрибутов default-impl и delegate-ref заключается в том, что во вто- ром случае компонент Spring сам может быть субъектом внедрения, применения аспектов и воздействия других механизмов Spring.

Источник:   Уоллс К., Spring в действии. – М.: ДМК Пресс, 2013. – 752 с.: ил.

По теме:

  • Комментарии