Páginas

domingo, 1 de janeiro de 2012

Injeção de dependências com Spring


Vamos ver o seguinte caso. O senhor Bilbo possui uma loja de roupas e quer identificar qual a profissão de cada indivíduo que entra na sua loja. Poderíamos ter diversos tipos de trabalhadores que trabalham de forma diferente, então vamos criar uma interface para que suporte as diferentes implementações dos trabalhadores. Nossa interface vai se chamar IWorker:

Interface IWorker:

packageorg.samples.spring.injection;

publicinterfaceIWorker{

publicvoidwork();


}

Basicamente é um trabalhador e ele trabalha. Agora vamos a uma implementação mais específica, vamos implementar uma classe trabalhador. Conversando com o Sr. Bilbo, descobrimos que ele adora programadores. Então, como é de preferência do dono da loja, vamos implementar o trabalhador programador.

Classe Programmer:

packageorg.samples.spring.injection.programmer;

importorg.samples.spring.injection.IWorker;

publicclassProgrammerimplementsIWorker{

privateStringlanguage;


publicStringgetLanguage(){

returnlanguage;

}

publicvoidsetLanguage(Stringlanguage){

this.language=language;

}

@Override

publicvoidwork(){

System.out.println("Programando em "+this.getLanguage());

}

}

Em outra conversa com o cliente, ficamos chocados quando nos contou que sua loja foi arrombada varias vezes. Então para ajudá-lo a verificar quais são os arrombadores criamos a classe concreta:

Classe Burglar:

packageorg.samples.spring.injection.burglar;

importorg.samples.spring.injection.IWorker;

publicclassBurglarimplementsIWorker{

privateStringtool;


publicBurglar(Stringtool){

this.tool=tool;

}


publicStringgetTool(){

returntool;

}

publicvoidsetTool(Stringtool){

this.tool=tool;

}

@Override

publicvoidwork(){

System.out.println("Arrombando com "+this.getTool());

}

}

Depois de estipular as principais necessidades, vamos verificar o trabalho dos cliente da loja do Sr. Bilbo.

Vimos que as implementações até o momento foram desenvolvidas sobre interfaces. Isso é importante e já veremos o porque. O Spring necessita de um arquivo de configuração onde ficam mapeadas as classes a serem injetadas. Essas classes serão carregadas no container do Spring mas só serão instanciadas a partir da primeira requisição a classe desejada. Abaixo segue o xml utilizado no projeto.

hello.xml

<?xml version="1.0" encoding="UTF-8"?>

DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>

<bean id="programmer" class="org.samples.spring.injection.programmer.Programmer">

<property name="language" value="Java"/>

bean>

<bean id="burglar" class="org.samples.spring.injection.burglar.Burglar">

<constructor-arg value="pé-de-cabra"/>

bean>

beans>

Agora que criamos o arquivo de configuração, vamos a ação. Depois vamos verificar o que significa cada item da mapeamento.

Segue abaixo a implementação da classe Main:

Class Main:

packageorg.samples.spring;

importorg.samples.spring.injection.IWorker;

publicclassMain{

/**

*@paramargs

*/

publicstaticvoidmain(String[]args){

BeanFactoryfactory=newXmlBeanFactory(newFileSystemResource("hello.xml"));

IWorkerworker1=(IWorker)factory.getBean("programmer");

worker1.work();


IWorkerworker2=(IWorker)factory.getBean("burglar");

worker2.work();

}

}

E quando executamos esse código temos a seguinte saida:

Programando em Java

Arrombando com pé-de-cabra

Anteriormente mencionei que a programação para interfaces é muito importante. Podemos ver uma pequena parte do benefício que esta técnica nos traz. No nosso exemplo recuperamos dois beans do container Spring, mas nossa classe Main, faz a menor idéia de que classes são, apenas que implementam a interface IWorker.

Pode parecer simples, mas abstraimos toda a parte de implementação de um componente específico de nosso exemplo para uma interface. Assim nossa classe Main não precisa se preocupar em qual classe foi retornada pelo container, mas apenas em executar o serviço. Se fosse necessário criar uma nova classe para substituir a classe Programmer, esta classe implementaria a interface IWorker e nossa classe Main nunca ficaria sabendo.

Bom, agora vamos dar uma olhada no código do descritor que criamos para nossa aplicação.

<bean id="programmer" class="org.samples.spring.injection.programmer.Programmer">

<property name="language" value="Java"/>

bean>

Ao criarmos inserirmos a tag bean em nosso descritor, definimos que vamos declarar um bean que será carregado no container. A propriedade id define um identificador para o nosso bean e a propriedade class define qual classe será carregada.

Logo depois temos a tag property. Esta tag define que estamos setando um valor a um atributo de nosso bean. Ou seja, no caso, a tag name referência qual a atributo será setado, e a propriedade value, o valor propriamente dito que será setado.

Agora vamos ver nosso outro bean.

<bean id="burglar" class="org.samples.spring.injection.burglar.Burglar">

<constructor-arg value="pé-de-cabra"/>

bean>

Podemos perceber que é praticamente igual ao bean anterior. O que diferencia é a tag constructor-arg. Bom, essa tag nos diz que será setado um valor no construtor da nossa classe, no caso, Burglar.

Agora vamos ver uma situação interessante. O Spring possui escopos para a carga de sua classes. Ou seja, podemos dizer que nossas classes são carregadas de formas diferentes. Por default, o Spring utiliza o escopo Singleton. Então o bean será carregado apenas uma vez, e será uma para toda a aplicação. Vamos fazer um teste.

Inclua as seguintes linha no método main anteriormente listado:

((Programmer)worker1).setLanguage("Ruby");

IWorkerworker3=(IWorker)factory.getBean("programmer");

worker3.work();

Se rodar o exemplo novamente vamos ter a seguinte saida:

Programando em Java

Arrombando com pé-de-cabra

Programando em Ruby

Bom, nós alteramos o estado de um bean adquirimos do BeanFactory, mas como o escopo é Singleton, só existe uma instância do bean Programmer.

Podemos alterar esse comportamento incluindo a propriedade scope=”prototype” na tag bean de nosso bean Programmer. Prototype diz que para cada chamada para recuperar uma instância do bean Programmer, temos uma nova instância.

O novo resultado será:

Programando em Java

Arrombando com pé-de-cabra

Programando em Java

Existem outros escopos para Spring (request, session e global-session), mas os restantes são para Conteiners Web.

Ficamos por aqui com este tutorial, mas em breve estarei de volta com novos tutos.

Nenhum comentário:

Postar um comentário