Técnico

Injeção de dependência com Spring

Começando com o Spring

Este post visa ajudar na compreensão de um dos conceitos mais difundidos de OO e que ajudaram a tornar o Spring um dos frameworks mais utilizados na comunidade: Inversão de Controle. Um dos casos de Inversão de Controle é a Injeção de Dependências, e é dela que trataremos neste post.

Inversão de Controle e Injeção de Dependência

Imagine que queremos implementar um serviço web que disponibiliza algumas informações do sistema, recuperando algumas informações do banco de dados e executa algumas regras de negócio sobre elas. Para isso nosso serviço deveria de alguma maneira acessar o banco de dados da aplicação, por exemplo através de um ‘DAO‘, e a partir do DAO recuperar os dados necessários. Com os dados em mãos nosso serviço consegue delegar para alguma classe do sistema executar as regras de negócio que se apliquem, por exemplo um ‘Manager‘.

Para este exemplo o mais direto seria instanciar em nosso serviço um objeto da classe DAO e um objeto da classe Manager para que possam ser usados. Em teoria isso seria uma tarefa simples, porém no nosso dia-a-dia de desenvolvimento sabemos que configurar um DAO que acessa o banco de dados não é tão simples assim. Além do DAO, nosso serviço precisa instanciar também uma ‘SessionFactory‘, configurar uma conexão com o banco e outras coisas dependendo da implementação da JPA que se está utilizando. O mesmo vale para a classe de negócio, pois tal classe pode precisar de outras classes mais especializadas e sendo assim, todas essas classes também devem ser intanciadas em nosso serviço.

A situação fica pior se quisermos criar um novo serviço que por exemplo usa o mesmo DAO, mas use classes de negócio diferente. Toda a lógica de instanciar o DAO deve ser replicada no novo serviço.

Agora imagine que temos o mesmo problema para ser resolvido, implementar um serviço web, porém ao invés de nos preocupar em instanciar as classes que iremos precisar, elas já estivessem instanciadas e configuradas corretamente e inclusive ‘setadas‘ em nosso serviço prontas para uso. Esse padrão de deixar a responsabilidade de criar e configurar classes que estarão disponíveis na aplicação fora das classes que as usam se chama Inversão de Controle. Estamos tirando a lógica de criação de dependências das classes chamadas, e deixando a cargo das classes que chamam. Quando temos as dependências criadas e configuradas, e temos as classes que irão usá-las e ‘injetamos‘ as denpendências nas mesmas, temos o que chamamos de Injeção de Dependências.

No Spring o responsável por criar e configurar as classes que serão usadas é chamado de ‘BeanFactory‘, e tais classes são chamadas de ‘beans‘. ‘Beans’ do Spring estão disponíveis no ‘container‘ e podem ser recuperados a qualquer momento dentro do contexto da aplicação. No Spring o contexto da aplicação é representado pelo ‘ApplicationContext‘ que pode ser criado de várias maneiras. Iremos abordar isso mais pra frente.

Definindo um ApplicationContext

Tudo no Spring começa a partir de um contexto da aplicação. Por isso precisamos criar um antes de utilizarmos os princípios de inversão de controle e injeção de dependência. Dentro das maneiras de criar um ApplicationContext usaremos a criação por XML, que é simples o suficiente para começarmos a usar o Spring. Para isso defina o seguinte arquivo XML no seu projeto com o nome de ‘applicationContext.xml‘:

[cc_xml]


[/cc_xml]

Lembre que para utilizar o framework, você precisa ter os jars do Spring referenciados no seu projeto. Estamos usando a versão 3, que podem ser encontrados no próprio site do Spring (http://www.springsource.org/download). A princípio só usaremos os módulos ‘spring-beans‘ e ‘spring-context‘.

Dica: se o seu projeto está usando o maven, você pode apenas adicionar as seguintes dependências que o maven se encareguará de referenciá-las no seu projeto:

[cc_xml]

org.springframework
spring-context
3.0.2.RELEASE


org.springframework
spring-beans
3.0.2.RELEASE

[/cc_xml]

Crie uma classe para executarmos alguns testes, como por exemplo a classe abaixo com um método ‘main‘:

[cc_java]
package br.com.;

public class ClasseMain {

public void main(String… args) {

}
}
[/cc_java]

Dentro do nosso método ‘main’, iremos criar um novo ApplicationContext a partir do XML que definimos acima:

[cc_java]
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(“applicationContext.xml”);
[/cc_java]

Definindo nossos primeiros Beans

Iremos definir agora os beans que estarão disponíveis em nossa aplicação. Existem duas maneiras de se fazer isso. A primeira e que era usada até a versão 2.0 do Spring é através de XML. Para isso basta definir os beans dentro do nosso aaplicationContext.xml. Veremos exemplo em breve. Uma segunda maneira que foi introduzida a partir da versão 2.5 do Spring, é através de anotações. Para usarmos anotações precisamos colocar uma linha a mais no applicationContext.xml:

[cc_xml]

[/cc_xml]

No atributo ‘base-package‘ você deve colocar o caminho do pacote onde ficarão as classes dos seus beans.

Precisamos agora de classes para serem nossos beans. Vamos criar uma classe qualquer apenas para entender a injeção de dependências:

[cc_java]
package br.com.;

public class MockTestClass {

private String a;

public String getA() {return a;}
public void setA(String a) {this.a = a;}
}
[/cc_java]

E uma outra classe que dependa de MockTestClass:

[cc_java]
package br.com.;

public class MockTestClass2 {

private MockTestClass mockBean;

public MockTestClass getMockBean() {return mockBean;}

public void setMockBean(MockTestClass mockBean) {this.mockBean = mockBean;}
}
[/cc_java]

Repare que as classes possuem atributos privados e seus respectivos ‘getters‘ e ‘setters‘. O Spring consegue injetar dependências diretamente nos atributos, mas para começar iremos usar o padrão de ‘getter‘ e ‘setter‘.

Vamos disponibilizar os beans de nossas duas classes em nosso ApplicationContext. Se você preferir usar XML, basta adicionar o seguinte dentro da tag ‘beans‘ do applicationContext.xml:

[cc_xml]



[/cc_xml]

Repare que a tag ‘bean‘ recebe dois parâmetros: ‘class‘ e ‘id‘. A tag ‘class‘ indica o caminho o absoluto do no bean, nesse caso temos o MockTesClass e MockTestClass2 como nossos beans. A tag ‘id‘ define um nome para os nossos beans, no caso ‘mockBean‘ e mockBean2‘. Usaremos esse nome para recuperá-los do container.

Caso você deseje usar anotações, basta adicionar o seguinte nas classes dos beans (não esqueça de adicionar a linha no applicationContext.xml que indica o pacote com os beans):

[cc_java]
@Component(“mockBeans”)
public class MockTestClass {
[/cc_java]

E para MockTestClass2:

[cc_java]
@Component(value = “mockBean2”)
public class MockTestClass2 {
[/cc_java]

Agora temos nossos dois beans disponíveis no container da aplicação. Para validar que tudo está criado corretamente, volte em nossa classe com método ‘main’ e adicione o seguinte código:

[cc_java]
MockTestClass mockBean = (MockTestClass) applicationContext.getBean(“mockBean”);
System.out.println(“Mock Bean: ” + mockBean);
[/cc_java]

Rode o código e repare que foi impresso no console o endereço do seu bean. Faça o mesmo para o segundo bean:

[cc_java]
MockTestClass2 mockBean2 = (MockTestClass2) applicationContext.getBean(“mockBean2”);
System.out.println(“Mock Bean 2: ” + mockBean2);
[/cc_java]

Adicione o seguinte código no final do método:

[cc_java]
System.out.println(“Mock Bean de Mock Bean 2: ” + mockBean2.getMockBean());
[/cc_java]

Rode e perceba que mockBean está nulo. Isso porque não falamos para o Spring que ele deveria injetar essa dependência em nosso bean de maneira automática. Existem algumas formas de se configurar esse comportamento. Seguindo nosso padrão até agora, vamos injetar a dependência pelo XML e via anotações, em um post futuro trataremos de injeção automática de dependências.

No XML modifique o ‘mockBean2‘ adicionando o seguinte trecho:

[cc_xml]

[/cc_xml]

Na tag ‘property‘ definimos uma propriedade do bean que deve ser gerenciada pelo Spring, nesse caso a propriedade é o ‘mockBean‘. Com isso o Spring irá invocar o ‘setMockBean‘ e injetar a dependência. Já com anotações basta anotar o ‘setter‘ de ‘mockBean‘ com ‘@Autowired‘. Dessa forma o Spring irá buscar por um bean com nome ‘mockBean’ no container e caso encontre este será injetado automaticamente:

[cc_java]
@Autowired
public void setMockBean(MockTestClass mockBean) {this.mockBean = mockBean;}
[/cc_java]

O resultado de ambas as maneiras é exatamente o mesmo. Para ter certeza, rode novamente nossa classe de teste, e repare no endereço impresso no console. Note que o endereço do ‘mockBean‘ é o mesmo tanto quando recuperamos direto do ApplicationContext, quanto do ‘mockBean2‘. Isso acontece porque por padrão, todo bean do Spring é ‘Singleton‘ dentro do contexto da aplicação. Iremos tratar de escopo de beans em um post futuro, mas se você deseja testar basta adicionar o seguinte para que seu bean seja criado sempre que requisitado:

No XML adicione ‘scope=”prototype”‘:

[cc_xml]


[/cc_xml]

Via anotação adicione ‘@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)‘ na classe:

[cc_java]
@Component
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class MockTestClass {
[/cc_java]

Rode a classe e repare que os endereções agora são diferentes.

Cobrimos o básico de injeção de dependência do Spring. Existem muitas outras maneiras de se injetar dependências nos beans, de se gerenciar criação de beans, de declarar beans enfim, vimos pouca coisa mas que já suficiente para começarmos a estudar alguns conceitos um pouco mais avançados de OO e entendermos melhor o funcionamente de alguns frameworks. A documentação do Spring é uma das melhores disponíveis na comunidade, por isso recomendo a consulta (http://www.springsource.org/documentation). Vamos nos aprofundar mais no funcionamento e diferentes usos do Spring em posts futuros.

7 Comments

  • Roberto Santos disse:

    Muito bom seu post, aliás gostaria de parabenizar a DClick como um todo pela excelente iniciativa vinda com o Agon que com certeza vai acabar ajudando muita gente com sua base de conhecimento.

    Já tentei várias vezes implementar Spring nas aplicações mas sempre esbarrava em um ponto ou outro e agora conseguí realmente entender o conceito por trás do container.

    Só fiquei com uma dúvida: Vou precisar manter o applicationContext também como um singleton na minha aplicação? Por exemplo, eu instanciei o applicationContext no main da aplicação porém tenho uma fábrica de Clientes em outra package que vai precisar recuperar beans do contexto porém essa fábrica não enxerga o main, qual a forma trivial de resolver isso?!?

    Grande abraço!

  • gustavo.moreira disse:

    Boa tarde Roberto! Muito Obrigado pelo feedback!!
    Isso mesmo Roberto, o applicationContext deve ser mantido em um singleto na aplicação. Uma maneira trivial de se fazer isso é extraindo a lógica de instanciar o applicationContext do main para alguma classe executada ao subir a aplicação, deixando o acesso à instância do applicationContext restrito pelo Singleton.
    Esse formato é bem válido para aplicações desktop. Quando a aplicação é WEB, costumamos deixar a instância do application context guardada com alguma classe já implementada pelo Spring, para garantir acesso concorrente e se certificar de que o application context continua ‘ThreadSafe’.

    Espero ter ajudado, perguntas e críticas são sempre bem vindas!
    Abraço!

  • […] todo os itens que deram pontos ao Gustavo: – Injeção de dependência com Spring – O que é o Maven, e seus primeiros passos com a ferramenta – Criando seu primeiro projeto Maven – […]

  • Carlos disse:

    Post bem elucidativo. Parabéns!

  • Paulo Vitor Faria Fortes rezende disse:

    Parabéns pelo post Gustavo!!

    Estou iniciando em Spring e este foi o melhor post introdutório de DI que eu li até agora!

    Obrigado!

  • gustavo.moreira disse:

    Obrigado Paulo 🙂

  • Cristiano disse:

    Muito bom o artigo, porém ainda tenho dúvidas de qual a melhor maneira de trabalhar com o Spring integrado ao Hibernate ou ao JPA+Hibernate.

    Poderia disponibilizar o fonte?

    Abraço!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Compartilhe isso: