Главная » Spring » Основы замещения методов Spring

0

Вам нравятся шоу иллюзионистов? Фокусники используют лов- кость рук и отвлечение внимания, чтобы прямо на наших глазах де- лать казалось бы невозможные вещи. Один из наших излюбленных трюков – когда фокусник помещает своего ассистента в ящик, кру- жит вокруг ящика, бубнит какие-то магические слова, потом… вуаля! Ящик открывается, и в нем вместо ассистента оказывается тигр.

Так случилось, что Гарри, обещанный нами фокусник, только что включился в конкурс талантов «Spring Idol» и будет исполнять наш любимый фокус. Позвольте мне представить вам Гарри – первый из класса Magician, определение которого приводится в листинге 3.1.

Листинг 3.1. Фокусник и его черный ящик

package com.springinaction.springidol;

public  class  Magician  implements  Performer  { public  Magician()  {}

public void perform() throws PerformanceException { System.out.println(magicWords); System.out.println("The  magic  box  contains…");

System.out.println(magicBox.getContents()); // Исследует содержимое

}                                                                                         // ящика

// внедрение

private  MagicBox  magicBox;

public  void  setMagicBox(MagicBox  magicBox)  {

this.magicBox = magicBox; // Внедрение волшебного ящика

}

private String magicWords;

public  void  setMagicWords(String  magicWords)  { this.magicWords  =  magicWords;

}

}

Как видите, класс Magician имеет два свойства, которые могут быть установлены с помощью Spring DI. Фокуснику нужны какие-нибудь магические слова, чтобы магия сработала, поэтому желательно уста- новить значение свойства magicWords. Но что более важно, фокуснику нужно передать черный ящик через свойство magicBox. Коль скоро речь зашла о черном ящике, его реализацию можно увидеть в лис- тинге 3.2.

Листинг 3.2. Реализация черного ящика с симпатичной ассистенткой внутри… или нет?

package com.springinaction.springidol;

public class MagicBoxImpl implements MagicBox {

public  MagicBoxImpl()  {}

public  String  getContents()  {

return "A beautiful assistant"; // Симпатичная  ассистентка в ящике

}

}

Ключевым методом в классе MagicBoxImpl, на который следует обратить внимание, является метод getContents(). Заметьте, что он всегда возвращает строку «A beautiful assistant» (симпатичная ас- систентка), но, как вскоре будет показано, в действительности все выглядит не так, как кажется. Однако, прежде чем я покажу фокус, взгляните, как Гарри и его черный ящик связаны с контекстом при- ложения:

<bean id="harry"

class="com.springinaction.springidol.Magician">

<property  name="magicBox"  ref="magicBox"  />

<property  name="magicWords"  value="Bippity  boppity  boo"  />

</bean>

<bean   id="magicBox"

class="com.springinaction.springidol.MagicBoxImpl" />

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

ApplicationContext ctx = … // загрузка контекста Spring Performer magician = (Performer) ctx.getBean("harry"); magician.perform();

Когда этот фрагмент извлечет компонент harry из контекста при- ложения и вызовет метод perform(), вы увидете то, что ожидаете увидеть:

Bippity  boppity  boo

The   magic   box   contains… A   beautiful  assistant

Вывод этих строк не должен стать сюрпризом для вас. В конце концов, метод getContents() объекта MagicBoxImpl всегда должен воз-

вращать строку «A beautiful assistant». Но, как я сказал, это лишь поддразнивание. Гарри пока не выполняет настоящий фокус. Одна- ко пора бы начать представление, так что давайте настроим XML- конфигурацию, чтобы она выглядела следующим образом:

<bean   id="magicBox"

class="com.springinaction.springidol.MagicBoxImpl">

<replaced-method name="getContents" replacer="tigerReplacer"   />

</bean>

<bean id="tigerReplacer" class="com.springinaction.springidol.TigerReplacer"    />

Сейчас в компоненте magicBox имеется элемент <replaced-method> (см. рис. 3.6). Как следует из названия, этот элемент используется для замены метода новой реализацией. В данном случае атрибут name указывает имя замещаемого метода getContents(). А атрибут replacer ссылается на компонент tigerReplacer, реализующий замену.

Рис. 3.6. Использование элемента <replaced-method>

для внедрения облегчает замещение метода getContents(), возвращающего строку «A beautiful assistant», иной реализацией, которая создает тигра

Вот где настоящая ловкость рук. Компонент tigerReplacer – это объект класса TigerReplacer, который определен в листинге 3.3.

Листинг 3.3. TigerReplacer, который подменяет реализацию метода

package com.springinaction.springidol; import java.lang.reflect.Method;

import org.springframework.beans.factory.support.MethodReplacer; public class TigerReplacer implements MethodReplacer {

public Object reimplement(Object target, Method method, // Подмена Object[]  args)  throws  Throwable  {                         // метода

return "A ferocious tiger"; // Помещает тигра в ящик

}

}

Класс TigerReplacer реализует интерфейс MethodReplacer, требую- щий реализации единственного метода reimplement(). Этот метод принимает три аргумента: объект, в котором будет производиться замещение метода, метод, подлежащий замещению, и массив аргу- ментов, принимаемых методом. В нашем случае аргументы отсут- ствуют, но их можно передать при необходимости.

Тело метода reimplement() фактически становится новой реализа- цией метода getContents() черного ящика. В нашем примере един- ственное, что требуется, – это вернуть строку «A ferocious tiger» (свирепый тигр). Фактически прежнее содержимое ящика заменя- ется тигром, как показано на рис. 3.6.

Если теперь вызвать метод perform(), он выведет следующее:

Bippity  boppity  boo

The   magic   box   contains… A  ferocious  tiger

Та-да! Симпатичная ассистентка пропала, и вместо нее появился свирепый тигр, причем без изменения существующей реализации MagicBoxImpl. Фокус был успешно выполнен с помощью элемента

<replaced-method>.

Стоит отметить, что хотя MagicBoxImpl имеет конкретную реали- зацию метода getContents(), метод getContents() точно так же можно было бы объявить абстрактным. В самом деле, прием внедрения метода с успехом может использоваться, когда фактическая реали- зация замещаемого метода не известна до момента развертывания. К этому моменту класс с замещающей реализацией метода можно оформить в виде JAR-файла и поместить его в библиотеку классов (classpath) приложения.

Замещение методов – это, конечно, изящный трюк. Но есть более специализированная форма внедрения методов, позволяющая сре- де выполнения связывать компоненты через метод чтения. Посмот- рим, как выполняется внедрение через метод чтения в компонентах Spring.

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

По теме:

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