Статьи

Spring Framework: три метода жизненного цикла Spring Bean

При использовании термина «жизненный цикл» ребята в Spring ссылаются на конструирование и уничтожение ваших бобов, и обычно это связано с созданием и уничтожением Spring Context. Бывают случаи, когда управление жизненным циклом вашего бина не является тривиальной задачей, так как ему необходимо выполнить собственную внутреннюю настройку. Это обычно верно, когда ваш компонент должен взаимодействовать с внешней системой, включая: загрузку файла, открытие сокета или чтение некоторых данных из базы данных. Неважно, что это такое, чтобы решить эту проблему, все, что нужно, чтобы Spring вызывал ваш бин, когда он одновременно загружает Spring Context и закрывает его.

Для этого у Spring есть три способа вызова вашего кода во время инициализации и завершения работы. Эти:

  • Программно, обычно называемый «интерфейсные обратные вызовы».
  • Декларативно для каждого компонента, называемого «обратным вызовом метода».
  • Декларативно, применяя один и тот же метод обратного вызова по умолчанию ко всем bean-компонентам.

Интерфейсные обратные вызовы — это то, что я описал ранее; однако, чтобы суммировать технику и убедиться, что Spring вызывает ваш бин во время установки и прерывания контекста Spring, ваш бин должен реализовать определенный интерфейс. В случае инициализации это InitializingBean а в случае выключения — DisposableBean . Если вам нужно больше узнать об этих методах, то здесь есть блог по InitializingBean и другой по DisposableBean .

Я на самом деле думаю, что название «обратные вызовы методов» несколько вводит в заблуждение, поскольку в действительности не описывает происходящее. Когда вы используете метод обратного вызова, вы делаете добавление метода в ваш компонент, на который вы затем ссылаетесь в своей конфигурации XML. Когда Spring читает конфигурацию, он обнаруживает, что есть bean-компонент типа X с методом, который должен вызываться при запуске, а другой — для вызова при завершении работы.

Теперь нам нужен сценарий, и поскольку одна из причин для методов обратного вызова bean-компонентов заключается в том, что вы можете инициализировать внешние системы, я собираюсь предположить, что вы работаете в компании прямого маркетинга и что вам дали работа по написанию одного из тех надоедливых приложений, которые набирают случайные числа посреди ночи и воспроизводят записанное сообщение получателю, сообщая им, как они могут получить кучу компенсаций за травмы, то есть наличные, предъявив иск какой-либо компании за несчастный случай они никогда не имели.

Идея состоит в том, что Dialer — это внешняя система, для которой вы должны написать контроллер. Когда контроллер запускается, он должен подключиться к номеронабирателю, а когда он выключается, отключиться.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
/**
   * Dial the number
   *
   * @param phoneNumber
   *            the phone number as a string
   * @return true if the number is dialed successfully
   */
  public boolean dial(String phoneNumber);
 
  /**
   * Play a message
   */
  public void playMessge();
 
  /**
   * Hang up the line...
   */
  public boolean hangUp();

DialerController определяется интерфейсом выше, и, как и следовало ожидать, он имеет несколько методов типа телефона, таких как dial(...) , playMessage() и hangUp() . Следующее, что нужно сделать, — это создать компонент, который реализует эти методы, что я и сделал ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Component
public class DialerControllerImpl implements DialerController {
 
  private boolean connected;
 
  @Override
  public boolean dial(String phoneNumber) {
 
    boolean retVal = false;
    if (isMiddleOfTheNight()) {
      testConnection();
      System.out.println("Dialing number: " + phoneNumber);
      retVal = true;
    }
 
    return retVal;
  }
 
  private boolean isMiddleOfTheNight() {
    return true;
  }
 
  @Override
  public void playMessge() {
    testConnection();
    System.out.println("Hello, do not hang up you may be entitled to...");
  }
 
  @Override
  public boolean hangUp() {
    testConnection();
    System.out.println("Hangup!");
    return true;
  }
 
  public void init() {
    connected = true;
    System.out.println("Connect to dialer");
  }
 
  public void destroy() {
    connected = false;
    System.out.println("Close connection to dialer");
  }
 
  private void testConnection() {
    if (connected == false) {
      throw new RuntimeException("Not connected to external system error");
    }
  }
}

playMessage() dial(...) , playMessage() и hangUp() не являются чем-то особенным; они проверяют, что компонент подключен к внешнему номеронабирателю, который он контролирует, и затем выполняет свою работу. Интересным моментом в этом классе являются методы init() и destroy() поскольку именно эти методы мы хотим, чтобы Spring вызывал при запуске и завершении работы соответственно.

Чтобы убедиться, что Spring действительно вызывает наш bean-компонент, нам нужно выполнить некоторые jiggery-pokery в XML-файле конфигурации Spring.

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
 
 
 <bean id="dialerController" class="example_2_lifecycle_management.method_based.DialerControllerImpl" init-method="init" destroy-method="destroy" />
 
</beans>

В этом примере я использую явную настройку bean-компонента (это означает, что вы можете игнорировать атрибут @Component в приведенном выше коде, так как он пока не используется, но это необходимо позже), и обратите внимание на конфигурацию bean-компонента: дополнительные атрибуты init-method и destroy-method . Они используются для определения имен методов вашего компонента, которые вы хотите, чтобы Spring вызывал при его инициализации и завершении работы. В этом примере они соответствуют методам init() и destroy() в классе DialerControllerImpl выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
@Test
  public void testLifeCycle_using_per_bean_declaration() {
 
    ctx = new ClassPathXmlApplicationContext("dialer.xml");
    ctx.registerShutdownHook();
 
    instance = ctx.getBean(DialerControllerImpl.class);
 
    if (instance.dial("555-1234")) {
      instance.playMessge();
      instance.hangUp();
    }
  }

Приведенный выше код демонстрирует простой модульный тест, который запускает код (это не настоящий тест, поскольку он ничего не утверждает). Здесь необходимо отметить, что после создания контекста приложения Spring я добавил вызов
registerShutdownHook() . Это потому, что вам нужно сообщить JVM, чтобы Spring вызывал ваш метод destroy() . Вы можете создать и обработать хук отключения самостоятельно, как я делал в своем блоге DisposableBean , и иногда в этом есть преимущества, но об этом в другой день.

Теперь я могу услышать вопрос: «Что если я использую автопроводку?» Оказывается, что ребята в Spring добавили новую технику обратного вызова методов в Spring 3.1, которая называется «обратный вызов метода по умолчанию». Основная идея заключается в том, что вы объявляете имена методов инициализации и завершения работы в элементе <beans/> в верхней части XML-файла конфигурации, как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
  default-init-method="init"
  default-destroy-method="destroy">
 
 
 <!-- Enable autowiring -->
  <context:component-scan base-package="example_2_lifecycle_management.method_based" />
</beans>

При выборе техники жизненного цикла бина помните, что ребята из Spring рекомендуют выбирать обратные вызовы на основе методов, а не обратные вызовы на основе интерфейса. Причина этого заключается в том, что при выборе маршрута обратного вызова интерфейса вы привязываете свои bean-компоненты к Spring. Это может или не может быть проблемой, и все это действительно зависит от остальной части вашего приложения, так как использование многих других методов Spring также привязывает ваше приложение к Spring.

Ссылка: Spring Framework: Три метода жизненного цикла Spring Bean от нашего партнера JCG Роджера Хьюза в блоге Captain Debug’s Blog .