Back-End

26 jul, 2013

Três técnicas de ciclo de vida do Beans no Spring

Publicidade

Ao usar o termo “ciclo de vida”,  os caras do Spring estão se referindo à construção e à destruição de seus beans e, geralmente, isso é em relação à construção e à destruição do Spring Context. Há aquelas ocasiões quando a gestão do ciclo de vida do seu bean não é uma tarefa trivial porque há a necessidade de ele realizar o seu próprio conjunto interno. Isso geralmente é verdadeiro quando o bean tem que interagir com um sistema externo incluindo: carregar um arquivo, abrir um socket ou ler alguns dados do banco de dados. Realmente não importa o que seja, para resolver esse problema, tudo que você precisa é que o Spring  chame seu bean quando ele estiver carregando e fechando o Spring Context.

Para isso, o Spring tem três formas de chamar seu código durante a inicialização e o encerramento, que são as seguintes:

  • Programaticamente, normalmente chamada de “interface callbacks”.
  • Declarativa em uma base por bean, chamada de “método callback”.
  • Declarativa, aplicando o mesmo método callback padrão para todos os beans.

Interface callbacks são algo que eu descrevi antes, no entanto, para resumir a técnica e para garantir que o Spring chame seu bean durante a configuração e o encerramento do Spring Context, o bean tem que implementar uma interface específica. No caso de inicialização, é InitializingBean e, no caso de encerramento, é DisposableBean. Se você precisa saber mais sobre essas técnicas, há um artigo sobre InitializingBean e outro sobre DisposableBean.

Eu realmente acho que o nome “Método callbacks” é um pouco enganador, uma vez que não descreve realmente o que está acontecendo. O que você está fazendo quando usa um método callback é a adição de um método para o seu bean, que você depois referencia na sua configuração XML. Quando o Spring lê a configuração, ele descobre que há um bean do tipo X, com um método que precisa chamar na inicialização e outro que precisa chamar no encerramento.

O que precisamos agora é de um cenário e pelo fato de uma das razões para os métodos callback de bean é que você possa inicializar sistemas externos. Vamos imaginar que você esteja trabalhando para uma empresa de marketing direto, e que lhe foi dado o trabalho de escrever um desses aplicativos irritantes que disca números aleatórios no meio da noite e toca uma mensagem gravada para o receptor dizendo-lhes como eles podem obter compensação de danos pessoais, dinheiro, processando alguma empresa por um acidente que eles nunca tiveram.

bean

A ideia é que o Dialer seja um sistema externo para o qual você tem que escrever o controller. Quando o controller inicia, ele tem que se conectar ao Dialer e, quando ele desliga, se desconectar.

[java]

/**
* 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();

[/java]

O DialerController é definido pela interface acima e, como seria de esperar, tem alguns métodos de tipo de telefone, tais como dial(...), playMessage() e hangUp(). A próxima coisa a fazer é criar um bean que implementa esses métodos, o que eu fiz abaixo.

[java]

@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");
}
}
}

[/java]

Os métodos dial(...), playMessage() e hangUp() não têm nada de especial; eles verificam se o bean está conectado ao dialer externo que ele está controlando e, em seguida, faz o seu trabalho. O ponto interessante sobre essa classe são os métodos init() e destroy(), já que esses são os métodos que queremos que o Spring chame durante a inicialização e o encerramento, respectivamente.

bean_2

Para garantir que o Spring ligue para o nosso bean, precisamos fazer alguma trapaça na configuração do Spring XML.

[xml]

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<bean id="dialerController" init-method="init" destroy-method="destroy" />

</beans>

[/xml]

Nesse exemplo, estou usando a configuração de bean explícito, (o que significa que você pode ignorar o atributo @Component no código acima, já que ele não é usado agora, mas será necessário mais tarde), e a coisa a se notar sobre o bean de configuração são os atributos adicionais init-method e destroy-method. Eles são usados ​​para definir os nomes dos métodos de seu bean que você quer que o Spring chame quando inicializar e desligar. Nesse exemplo, eles correspondem aos métodos init() e destroy() na classe DialerControllerImpl acima.

[java]

@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();
}
}

[/java]

O código acima demonstra um simples teste unitário que executa o código (não é um teste verdadeiro, uma vez que não afirma nada). O principal ponto a ser observado aqui é que depois de criar o Spring Application Context, eu adicionei uma chamada registerShutdownHook(). Isso acontece porque você precisa dizer a JVM para dizer ao Spring para chamar o método destroy(). Você pode criar e manipular o desligamento como eu fiz no meu artigo DisposableBean e às vezes há vantagens em fazê-lo, mas falarei mais sobre isso outro dia.

A pergunta que eu posso ouvir agora é “se eu estiver usando autowiring?” Verifica-se que os caras do Spring adicionaram uma nova técnica declarativa de método callback no Spring 3.1, chamado de “método callback padrão”. A grande ideia aqui é que você declare os nomes dos métodos de inicialização e encerramento no elemento <beans/> no topo do seu arquivo de configuração XML, conforme mostrado abaixo:

[xml]

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
default-init-method="init"
default-destroy-method="destroy">

<!– Enable autowiring –>
<context:component-scan base-package="example_2_lifecycle_management.method_based" />
</beans>

[/xml]

O resultado disso é que, quando o Spring for carregado, ele vai verificar todos os seus beans, em busca de métodos com esses nomes. Se eles existirem, então ele vai chamá-los no momento apropriado.

Se você alterar o nome do arquivo de configuração para apontar o descrito acima e re-executar o mesmo teste unitário com este código, ainda vai funcionar. Desta vez, ele é autowired usando a anotação @Component, que eu ignorei acima.

[java]

@Test
public void testLifeCycle_using_default_declaration() {

ctx = new ClassPathXmlApplicationContext("dialer2.xml");
ctx.registerShutdownHook();

instance = ctx.getBean(DialerControllerImpl.class);

if (instance.dial("555-1234")) {
instance.playMessge();
instance.hangUp();
}
}

[/java]

Ao selecionar a técnica do ciclo de vida do bean, lembre que os caras do Spring recomendam que você escolha callbacks baseados em métodos em vez de callbacks baseados em interface. A razão para isso é que, ao escolher a rota de interface callback, você amarra seus beans ao Spring. Isso pode ou não ser um problema, e tudo realmente depende do resto da sua aplicação, assim como o uso de muitas outras técnicas do Spring também vai amarrar seu aplicativo ao Spring.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.captaindebug.com/2013/01/three-spring-bean-lifecycle-techniques.html#.URANQ799Lng