Injeção de dependências com Spring

Postado por em   ●   13 comentários

Gostaria de conhecer mais sobre injeção de dependências do ponto de vista do Spring Framework?

Esse é um conceito que ainda causa muita confusão na cabeça dos desenvolvedores. Quero aqui clarear esse assunto e trazer mais tranquilidade para quem está começando.

Continue lendo para aprender mais sobre:

  • O que é injeção de dependências (Dependency Injection – DI)?
  • Quais os pontos de injeção do Spring?
  • Para o Spring, o que torna uma classe elegível a ser injetada e o que são métodos produtores?
  • O que são os Qualificadores e como qualificar um bean Spring?
  • Quais os escopos possíveis de um bean?
  • Como utilizar a JSR 330?

Nos próximos minutos esse assunto já estará bem mais claro para você. :)

O que é injeção de dependências?

Injeção de dependências (ou Dependency Injection – DI) é um tipo de inversão de controle (ou Inversion of Control – IoC) que dá nome ao processo de prover instâncias de classes que um objeto precisa para funcionar.

Vamos supor uma classe como abaixo:

Como a propriedade repositorio é uma dependência de ClienteServico, então, segundo o conceito de injeção de dependências, uma instância dessa propriedade deve ser provida.

Para manter o conceito de injeção não podemos utilizar a palavra reservada new como abaixo:

Isso porque injeção é algo que espera-se que venha de fora para dentro.

A pergunta agora é: Por onde essa instância vai vir?

Isso vamos ver em um próximo tópico, sobre pontos de injeção.

O importante agora é entender que dessa forma nós conseguimos programar voltados para interfaces e, com isso, manter o baixo acoplamento entre as classes de um mesmo projeto.

Com certeza, essa característica é uma grande vantagem para a arquitetura do seu sistema. Sendo assim, esse é um conceito que merece a atenção da sua parte.

Pontos de injeção

Podemos considerar como pontos de injeção qualquer maneira que possibilite entregar a dependência para um objeto.

Os dois mais comuns são pelo construtor:

… e pelo método setter:

Provavelmente você já utilizou muito essas duas formas de injeção de dependências e esteja se perguntando:

Alexandre, injeção de dependências é só isso?

Sim, só. Esperava algo mais complicado?

A aplicação do conceito é bem simples mesmo, mas a grande questão vem quando queremos a automatização disso. E é aqui que o Spring entra.

Pontos de injeção no Spring

Apesar da aplicação do conceito ser simples, a automatização é bem complexa. Para a nossa felicidade, temos o Spring Framework que nos ajuda com esse trabalho.

Ele já tem toda a automatização da aplicação desse conceito de injeção de dependências.

Precisamos entender é como configurá-lo e quais são os pontos de injeção que ele usa.

Configurando o Spring

Nesse artigo, a configuração que irei fazer não é para o ambiente web.

Caso você queira mais detalhes sobre como configurar uma aplicação web com o Spring, você pode baixar o e-book Desenvolvimento Web com Spring Boot.

Aqui, vou utilizar essa configuração:

Basta que você crie a classe Aplicacao no seu projeto. Repare que, para a configuração acima funcionar, é preciso que as outras classes como, por exemplo, a ClienteServico estejam no mesmo pacote ou em um sub-pacote da classe Aplicacao.

E a única dependência que é preciso ter no Maven é esta:

Marcando um ponto como injetável no Spring

No Spring, para marcar os pontos de injeção dentro da sua classe, você utiliza a anotação @Autowired.

Você pode utilizar essa anotação em:

  • Propriedades;
  • Construtores; e
  • Métodos (mais comumente, os setters)

Utilizando a anotação nas propriedades, seria:

No construtor:

Por último, com métodos, seria assim:

É possível também misturar. Você pode, por exemplo, marcar uma propriedade e um construtor:

O que torna uma classe elegível de ser injetada

Para que uma instância do tipo ClienteRepositorio possa ser injetada em algum dos pontos de injeção é preciso que ela se torne um bean Spring.

Fazemos isso anotando ela com a anotação @Component ou com qualquer uma de suas especializações:

  • @Respository
  • @Service
  • @Controller

O Spring chama essas quatro anotações de estereótipos, sendo que as três últimas são como anotações “filhas” da anotação @Component.

A anotação @Component é a mais geral e as outras, que são @Repository, @Service e @Controller, são para usos mais específicos em componentes de persistência, serviço e controlador, respectivamente.

Apesar de termos esses quatro estereótipos, eles não tem especificidades, com exceção da anotação @Repository, que pode adicionar um pequeno comportamento relacionado a tradução das exceções de persistência.

Ainda assim, é aconselhável que você as utilize para especificar seus componentes de persistência, serviço e controlador, por dois motivos.

Primeiro que o uso delas torna mais fácil a associação com aspectos (AOP) e segundo porque não está descartada a possibilidade de, no futuro, elas ganhem tratamentos específicos do framework.

Inclusive a classe ClienteServico utilizada no método main da classe Aplicacao também precisa estar anotada. No caso, o melhor seria a anotação @Service.

Métodos produtores

As vezes precisamos ter beans cujas classes nós não podemos anotar.

Vamos supor uma classe ArquivoNuvem que não está em nosso projeto e que gostaríamos que ela fosse passível de ser injetada. Como ela está em um outro projeto, não podemos anotar ela com estereótipo algum.

O que fazer nesse caso?

Podemos criar um método produtor utilizando a anotação @Bean dentro de uma classe de configuração. Veja:

Com a configuração acima, podemos injetá-la em nossa classe de serviço normalmente:

Qualificando um bean Spring

Como falamos, uma das vantagens da injeção de dependências é o baixo acoplamento que conseguimos com a possibilidade de programar voltado a interfaces.

Mas aí pode vir uma pergunta:

O que acontece se tivermos duas implementações da mesma interface?

É lançada a exceção NoUniqueBeanDefinitionException. Uma boa forma de resolver isso é qualificando nossos beans.

Veja como funciona:

Dessa forma, podemos agora injetar as duas implementações em nossas classes:

Ainda é possível utilizar qualificadores bem mais específicos do que isso, mas, para o objetivo do artigo que é injeção de dependências, isso é o suficiente.

Os escopos de um bean Spring

Podemos controlar o tempo de vida de um bean Spring. Utilizamos o conceito de escopos para isso.

No Spring temos sete escopos ativos, sendo que cinco são somente para o ambiente web.

Os escopos:

  • singleton (o padrão); e
  • prototype;

… são escopos gerais, e os escopos:

  • request;
  • session;
  • globalSession;
  • application; e
  • websocket

… servem somente ao ambiente web.

Tem mais um escopo que é o “thread”. Este não está ativo por padrão, mas é bem simples ativá-lo.

Você ainda pode criar os seus próprios escopos caso precise. Assim como fizemos na configuração do escopo de “view” utilizado na integração do JSF com o Spring.

Para especificar um escopo, nós colocamos o nome dele dentro da anotação @Scope. Olhe só:

JSR-330 e os beans Spring

Para quem quer ou precisa utilizar a JSR-330, o Spring possui compatibilidade.

Basta trocar a anotação @Autowired por @Inject e a anotação @Component pela @Named.

Veja como ficaria:

Lembrando que é preciso também incluir a dependência no Maven:

Conclusão

Espero que o assunto “injeção de dependências com Spring” tenha clareado em sua mente. :)

Foi um artigo onde conversamos sobre o que é injeção de dependências e as características que o Spring adiciona a esse conceito como, por exemplo, a forma como criamos beans, os qualificadores deles e os escopos que eles podem assumir.

Para quem quiser avançar mais um passo, tenho um convite aqui que é para o download do e-book Desenvolvimento Web com Spring Boot:

FN013-CTA-Lead-Magnet--Img02

No mais, um abraço pra você e até uma próxima!

PS: o código-fonte do artigo está disponível em nosso GitHub: http://github.com/algaworks/artigo-spring-injecao-de-dependencias

É graduado em Sistemas de Informação, trabalha como instrutor na AlgaWorks e está no mercado de programação Java há mais de 8 anos, principalmente no desenvolvimento de sistemas corporativos.

13 comentários sobre “Injeção de dependências com Spring

  1. Alexandre -

    Parabéns pelo esclarecimento. Sugestão poderia falar sobre transação com spring, acho um tópico importante que muita gente tem dúvida.

    1. Alexandre Afonso Autor do post-

      É um bom tema mesmo. Coloquei na fila já. Obrigado Xará!

  2. Daniel -

    Ótimo artigo. Tenho uma duvida com esse pequeno exemplo :

    public class Pessoa {

    private String nome;

    @Autowired
    private Endereco endereco;

    // get e set…
    }

    public class Endereco {

    private String cidade;
    …..
    }

    desta forma como foi apresentada na Classe Pessoa é permitida/correto ou não tem sentido algum .

    Obrigado.

    1. Alexandre Afonso Autor do post-

      Valeu Daniel!

      Nesse seu caso as classes Pessoa e Endereco são entidades de banco de dados, correto?

      Se forem, acho que não faz sentido pelas formas que manipulamos nossas entidades. Irão ter casos que isso vai funcionar, mas acredito que em muitos outros você vai acabar tendo que sobrescrever a instância incluída pelo Spring.

      Imagino que no futuro teremos opções melhores para isso, mas por ora, eu não uso.

      Abraço!

  3. Ricardo Johannsen -

    Olá Alexandre, sou aluno do curso Spring expert, e gostaria muito de iniciar o projeto de um portal em Spring pra colocar os ensinamentos em prática. Para esse portal toda a lógica de negócios já está feita em EJB,toda essa lógica já está implementada em session beans(@Stateless), existe alguma forma prática de injetar meus beans de sessão stateless dentro dos meus controlers do spring?

  4. Alexandre Afonso Autor do post-

    Blz Ricardo?

    Tente algo como:

    @Configuration
    public class EJBConfig {

    @Bean
    public LocalStatelessSessionProxyFactoryBean umEjbQualquerService(){
    LocalStatelessSessionProxyFactoryBean ejbFactory = new LocalStatelessSessionProxyFactoryBean();
    factory.setBusinessInterface(UmEjbQualquerService.class);
    factory.setJndiName(“…”);
    return factory;
    }
    }

    Mas pense também na possibilidade de passar tudo para Spring. Se o único recurso que vc usa é EJB com persistencia, então acho que vale muito a pena. Vai ser quase que natural. É só configuração e anotações que vc vai precisar alterar.

    Abraço!

  5. Daniel -

    Olá Alexandre Afonso!
    Muito Obrigado pela resposta.

    Essas duas classes são entidade do Banco.

    Fiz um projeto com essa estrutura e tinha retirado @Autowired e está funcionado normalmente.
    Só que achei confuso na hora de passar os valores vindo da view : Como no exemplo ,meu Controller está assim :

    @RequestMapping(value=”InserirSpring”,method= RequestMethod.POST)
    public String adiciona(Pessoa pessoa) throws MongoClientException, ClassNotFoundException {

    collectionPessoas.insertOne(
    new Document(Constantes.NOME,pessoa.getNome())
    .append(Constantes.IDADE, pessoa.getIdade())
    .append(Constantes.PROFISSAO, pessoa.getProfissao())
    .append(Constantes.ENDERECO,
    new Document(Constantes.CIDADE,pessoa.getEndereco().getCidade())
    .append(Constantes.BAIRRO, pessoa.getEndereco().getBairro())
    .append(Constantes.ESTADO, pessoa.getEndereco().getEstado())
    )
    .append(Constantes.NASCIMENTO, pessoa.getData().getTime())

    );*/
    dao.inserir(pessoa);
    return “redirect:/pessoa/listagem”;
    }
    Como pode ser vista estou chamando os atributos de Endereco da seguinte forma : pessoa.getEndereco.getCidade,etc.

    está persistindo normalmente, só achei estranho que não precisei dar um New em Endereco para passar os paramentros.

  6. Alexandre Afonso Autor do post-

    Por nada Daniel.

    Nesse caso aí, o Spring MVC está fazendo isso por você.

    Você deve estar submetendo um formulário com as propriedades de endereço também, não é mesmo? Daí o Spring te ajuda instanciando suas subpropriedades (como a Endereco, no seu caso).

    Até!

  7. Daniel -

    Ola Alexandre Afonso, muito obrigado. Nesta caso esta já esta fazendo a injeção de dependência.

    { },.s

  8. Ricardo Johannsen -

    Olá Alexandre obrigado por responder. No exemplo que vc deu quem seria factory.setJndiName(“…”); isso depende do application Server?. bem que gostaria de mudar tudo para Spring, mas no momento é impossível, esse módulo que estou trabalhando é dependencia para no minimo uns outros 15 sistemas, tudo feito em ejb :(

  9. Alexandre Afonso Autor do post-

    Por nada.

    Sim, “setJndiName(…)” depende do application server.

    É… o seu caso é mais complicado mesmo de trocar para Spring. Até porque devem envolver várias pessoas também, né?

    Abraço!

  10. Emerson Lugo -

    Excelente post!! Parabéns.

Deixe um comentário