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!