Spring

Injeção de dependências com Spring

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:

public class ClienteServico {

  private ClienteRepositorio repositorio;

  public void salvar(Cliente cliente) {
    this.repositorio.salvar(cliente);
  }

  ...
}

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:

public class ClienteServico {

  private ClienteRepositorio repositorio = new ClienteRepositorioJPA();

  public void salvar(Cliente cliente) {
    this.repositorio.salvar(cliente);
  }

  ...
}

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:

public class ClienteServico {

  private ClienteRepositorio repositorio;

  public ClienteServico(ClienteRepositorio repositorio) {
    this.repositorio = repositorio
  }

  ...
}

public class Main {

  public static void main(String... args) {
    ClienteRepositorio clienteRepositorio = new ClienteRepositorioJPA();

    ClienteServico clienteServico = new ClienteServico(clienteRepositorio);
    ...
  }
}

… e pelo método setter:

public class ClienteServico {

  private ClienteRepositorio repositorio;

  ...

  public void setClienteRepositorio(ClienteRepositorio repositorio) {
    this.repositorio = repositorio;
  }
}

public class Main {

  public static void main(String... args) {
    ClienteRepositorio clienteRepositorio = new ClienteRepositorioJPA();

    ClienteServico clienteServico = new ClienteServico();
    clienteServico.setClienteRepositorio(clienteRepositorio);
    ...
  }
}

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:

public class Aplicacao {
  
  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      
    context.scan(Aplicacao.class.getPackage().getName());
    context.refresh();
      
    // Utilizando a nossa classe ClienteServico com as
    // injeções feitas pelo Spring.
    ClienteServico servico = context.getBean(ClienteServico.class);
    servico.salvar(new Cliente(1, "Aluno João da Silva"));
      
    context.close();
  }
}

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:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.5.RELEASE</version>
</dependency>

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:

public class ClienteServico {
  
  @Autowired
  private ClienteRepositorio repositorio;
  
  ...
}

No construtor:

public class ClienteServico {
  
  private ClienteRepositorio repositorio;
  
  @Autowired
  public ClienteServico(ClienteRepositorio repositorio) {
    this.repositorio = repositorio;
  }

  ...
}

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

public class ClienteServico {
  
  private ClienteRepositorio repositorio;
  
  ...

  // Caso você preferisse, esse método poderia se 
  // chamar também "configurarRepositorio", mas
  // o mais comum é criar um método setter mesmo.
  @Autowired
  public void setRepositorio(ClienteRepositorio repositorio) {
    this.repositorio = repositorio;
  }
}

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

public class ClienteServico {

  @Autowired
  private Notificador notificador;
  
  private ClienteRepositorio repositorio;
  
  @Autowired
  public ClienteServico(ClienteRepositorio repositorio) {
    this.repositorio = repositorio;
  }

  ...
}

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:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AplicacaoConfig {
  
  @Bean
  public ArquivoNuvem arquivoNuvem() {
    ArquivoNuvem nuvem = new ArquivoNuvem();
    return nuvem;
  }
}

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

public class ClienteServico {
  
  @Autowired
  private ArquivoNuvem nuvem;
  
  @Autowired
  private ClienteRepositorio repositorio;
}

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:

public interface Notificador {
  
  void notificar(Mensagem mensagem);

}

@Component
@Qualifier("importante")
public class Email implements Notificador {

  @Override
  public void notificar(Mensagem mensagem) {
    ...
  }
}

@Component
@Qualifier("urgente")
public class Sms implements Notificador {

  @Override
  public void notificar(Mensagem mensagem) {
    ...
  }
}

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class ClienteServico {
  
  @Qualifier("importante")
  @Autowired
  private Notificador importante;

  @Qualifier("urgente")
  @Autowired
  private Notificador urgente;
}

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ó:

@Scope("prototype")
@Service
public class ClienteServico {

  ...

}

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:

import javax.inject.Named;

@Named
public class ClienteRepositorioJPA implements ClienteRepositorio {
  
  ...
}


import javax.inject.Inject;
import javax.inject.Named;

@Named
public class ClienteServico {
  
  @Inject
  private ClienteRepositorio repositorio;

  ...
}

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

<dependency>
  <groupId>javax.inject</groupId>
  <artifactId>javax.inject</artifactId>
  <version>1</version>
</dependency>

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

Trabalha como programador Java há mais de uma década, principalmente com desenvolvimento de sistemas corporativos.Além de colaborar com o blog, também é instrutor de vários cursos de Java na AlgaWorks.

Olá,

o que você achou deste conteúdo? Conte nos comentários.

Junte-se a mais de 100.000 pessoas

Entre para nossa lista e receba conteúdos exclusivos e com prioridade

Você se Inscreveu com Sucesso!

Blog da AlgaWorks
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.