Tempo de leitura: menos de 1 minuto

Começamos a falar sobre Injeção de dependência em uma das nosso reuniões de melhoria continua, que ocorre todo mês aqui na empresa. Falei sobre essa reunião no post Git #1 – Por onde começar?, falei a pena ler, pois é uma das melhores reuniões que temos no nosso projeto.
Eu e o Wander Mudo estudamos o tema e apresentamos para a equipe na ocasião.
Optamos por estudar o Framework Guice (se diz Juice), por ser simples, pequeno, é uma implementação de referencia da JSR 330, além disso, já estava declarado na dependências de nosso projeto, pois um componente interno da empresa desenvolvido por outra equipe já usava.
Mas que é Injeção de dependências?
Injeção de dependência (Dependency Injection, em inglês) é um padrão de desenvolvimento de programas de computadores utilizado quando é necessário manter baixo o nível de acoplamento entre diferentes módulos de um sistema. Nesta solução as dependências entre os módulos não são definidas programaticamente, mas sim pela configuração de uma infraestrutura de software (container) que é responsável por “injetar” em cada componente suas dependências declaradas. A Injeção de dependência se relaciona com o padrão Inversão de controle mas não pode ser considerada um sinônimo deste.
Motivação
Nossa motivação era o alto nível de acoplamento que tínhamos em nosso projeto, dificultando a confecção de testes e também a reutilização de código. Muitas vezes, também notávamos código de negócio estava misturado com o código de infraestrutura (transações por exemplo).
Motivation:“Wiring everything together is a tedious part of application development.”
Outros pontos considerados foram:
- Usando static:
- Alto acoplamento;
- Não utiliza interface;
- Não é polimórfico.
- Factory:
- Como obter uma factory?
- Dependente da implementação;
- Códigos extras.
- Service Locator:
- Onde obter um Service Locator?
- Difícil de validar se os serviços solicitados estarão disponíveis.
JSR 330 : Injeção de Dependência para Java
É parte da CDI (Contexts and Dependency Injection for the JavaTM Platform), líderada Rod Jonhson/Bob Lee, publicado na JSR 299 em dezembro de 2009 como parte do Java EE 6 e, portanto, disponível para utilização por qualquer servidor Web ou servidor de aplicação Java EE 6, como por exemplo o GlassFish 3.
Independente de fazer parte do CDI, Injeção de dependência funciona no Java 5.
A JSR 330, disponibiliza algumas anotações cuja o Guice implementa, além disso, o Guice possui suas próprias anotações, abaixo segue tabela comparativa, comparando as anotações da JSR x Guice:
| JSR-330 javax.inject |
Guice |
| @Inject | @Inject |
| @Named | @Named |
| @Qualifier | @BindingAnnotation |
| @Scope | @ScopeAnnotation |
| @Singleton | @Singleton |
| Provider<T> | Provider<T> |
Nota: “Mixing JSR-330 and Guice annotations is supported, but discouraged.” Mistura das anotações são suportadas, no entanto não recomendadas.
Guice
Versão mais recente do Guice é a 4.0. As vantagens de no uso do Guice são as seguintes:
- Desacoplamento;
- Reusabilidade;
- Menor custo de manutenção;
- Códigos mais “limpos” (“less boilerplate”);
- Sem configuração de XML;
- Modularidade : guice-4.0.jar, guice-servlet-4.0.jar, guice-persist-4.0.jar, etc…;
- Facilidade de uso;
- Curva de aprendizado.
Para adicionar o Guice no projeto com o maven basta adicionar a seguinte dependência:
[code language=”xml”]
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.0</version>
</dependency>
[/code]
Modulo (Module)
Módulos definem como as dependências são resolvidas. É no modulo que configuramos, “ligamos” as coisas.
[code language=”java”]
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
/*
* Aqui definimos que a implementação padrão da interface
* TransactionLog é a DatabaseTransactionLog
*/
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
/*
* O mesmo ocorre aqui.
*/
bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
}
}
[/code]
Nota: Você pode ter mais que um modulo, você pode ter um modulo de produção e ou de teste por exemplo.
Injetor (Injector)
O Injetor é o responsável por descobre o que construir, resolver dependências, e “amarra” tudo junto. O Injetor depende que um modulo seja informado, para que ele possa resolver as dependências baseadas nas configurações feitas no modulo. Exemplo:
[code language=”java”]
final Injector injector = Guice.createInjector(new Modulo());
[/code]
Nota: “Guice builds an injector using configuration modules.”
Injetor criado, basta agora pedir a implementação para a interface:
[code language=”java”]
public static void main(String[] args) {
…
/*
* Agora que temos o injector , podemos construir objetos.
*/
CreditCardProcessor credCard = injector.getInstance(CreditCardProcessor.class);
…
}
[/code]
Bindings
Podemos classificar como binding todas as configurações feitas em um modulo. O Guice disponibiliza várias formas de bindings.
Linked Bindings
Linked Binding é a ligação direta entre uma interface e a classe concreta que a implementa.
[code language=”java”]
bind(IClasse.class).to(ClasseImpl.class);
[/code]
Nota: No exemplo abaixo, toda vez que for solicitado ao injetor uma instacia do tipo IClasse, se retornado uma instancia de ClasseImpl.
Just-In-Time Bindings
Este na minha opinião é o binding fácil e simples de usar. Aqui no projeto, 95% dos casos usamos o o Just-In-Time Binding.
Para usa-lo, basta da abrir a interface que se deseja configurar, e adicionar a anotação @ImplementedBy, passando por parâmetro a classe concreta. Prontinho… O Guice irá entender que ao solicitar uma instancia de IClasse na verdade você receberá Impl.
[code language=”java”]
@ImplementedBy(Impl.class)
public interface IClasse
[/code]
Bindings Annotations
É possível também configurar que toda a classe que possua a anotação “X”, retorne uma instancia de uma determinada classe conforme abaixo:
[code language=”java”]
bind(Clas.class).annotatedWith(Anotacao.class).to(Classe.class);
[/code]
Instance Bindings
Caso estejamos falando de uma instancia Singleton no sistema, podemos configurar uma classe para retornar sempre a mesma instancia.
[code language=”java”]
bind(Clas.class).annotatedWith(Integer.class).toInstance(l0001);
[/code]
@Provides Methods
Quando a dedução for muito complexa, podemos ainda usar um Provider. Ele funciona como uma Factory, existem dois tipo o Provides Methods e o Provider Binginds.
A implementação abaixo usamos o Provides Methods, que nada mais aí que um método, responsável por deduzir e instanciar a classe correta.
[code language=”java”]
@Provides
ClasseImpl getClasse(){
return new ClasseA();
}
[/code]
Provider Bindings
Já o Provider Bindings, é uma classe responsável por deduzir e instanciar a classe correta.
[code language=”java”]
private final Provider<Classe> classe;
classe.get();
[/code]
Construtctor Bindings
Existe a possibilidade também de indicar qual o construtor o Guice deve usar ao instancia a classe.
[code language=”java”]
bind(I.class).toConstructor(A.class.getConstructor(B.class));
[/code]
Conclusão
Como o conteúdo é um pouco extenso, falei mais sobre as anotações no próximo post.
Valeu galera.
Link permanente
Link permanente