Introdução ao Dagger 2
1. Introdução
Neste tutorial, daremos uma olhada em Dagger 2 - uma estrutura de injeção de dependência rápida e leve.
A estrutura está disponível para Java e Android, mas o alto desempenho derivado da injeção em tempo de compilação a torna uma solução líder para esta última.
2. Injeção de dependência
Como um pequeno lembrete,Dependency Injection é uma aplicação concreta do princípio mais genérico de Inversão de Controle, no qual o fluxo do programa é controlado pelo próprio programa.
É implementado por meio de um componente externo que fornece instâncias de objetos (ou dependências) necessárias para outros objetos.
E estruturas diferentes implementam injeção de dependência de maneiras diferentes. Em particular, uma das diferenças mais notáveis é se a injeção ocorre no tempo de execução ou no tempo de compilação.
A DI de tempo de execução geralmente é baseada em reflexões mais simples de usar, mas mais lentas em tempo de execução. Um exemplo dea run-time DI framework is Spring.
A DI em tempo de compilação, por outro lado, é baseada na geração de código. Isso significa que todas as operações pesadas são executadas durante a compilação. A DI em tempo de compilação adiciona complexidade, mas geralmente executa mais rapidamente.
O punhal 2 se enquadra nessa categoria.
3. Maven/Gradle Configuration
Para usar o Dagger em um projeto, precisaremos adicionarthe dagger dependency ao nossopom.xml:
com.google.dagger
dagger
2.16
Além disso, também precisaremosinclude the Dagger compiler usados para converter nossas classes anotadas no código usado para as injeções:
org.apache.maven.plugins
maven-compiler-plugin
3.6.1
com.google.dagger
dagger-compiler
2.16
Com essa configuração, o Maven produzirá o código gerado emtarget/generated-sources/annotations.
Por esse motivo,we likely need to further configure our IDE se quisermos usar algum de seus recursos de auto-completar de código. Alguns IDEs têm suporte direto para processadores de anotação, enquanto outros podem precisar que adicionemos esse diretório ao caminho de construção.
Como alternativa, se estivermos usando o Android com Gradle, podemos incluir as duas dependências:
compile 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
Agora que temos Dagger em nosso projeto, vamos criar um aplicativo de amostra para ver como funciona.
4. Implementação
Para nosso exemplo, vamos tentar construir um carro injetando seus componentes.
Agora,Dagger uses the standard JSR-330 annotations em muitos lugares, sendo um@Inject.
Podemos adicionar as anotações aos campos ou ao construtor. Mas, comoDagger doesn’t support injection on private fields, vamos para injeção de construtor para preservar o encapsulamento:
public class Car {
private Engine engine;
private Brand brand;
@Inject
public Car(Engine engine, Brand brand) {
this.engine = engine;
this.brand = brand;
}
// getters and setters
}
A seguir, implementaremos o código para realizar a injeção. Mais especificamente, criaremos:
-
amodule, que é uma classe que fornece ou constrói as dependências dos objetos, e
-
acomponent, que é uma interface usada para gerar o injetor
Projetos complexos podem conter vários módulos e componentes, mas como estamos lidando com um programa muito básico, um de cada é suficiente.
Vamos ver como implementá-los.
4.1. Módulo
Para criar um módulo,we need to annotate the class with the @Module annotation. Esta anotação indica que a classe pode disponibilizar dependências para o contêiner:
@Module
public class VehiclesModule {
}
Então,we need to add the @Provides annotation on methods that construct our dependencies:
@Module
public class VehiclesModule {
@Provides
public Engine provideEngine() {
return new Engine();
}
@Provides
@Singleton
public Brand provideBrand() {
return new Brand("example");
}
}
Além disso, observe que podemos configurar o escopo de uma determinada dependência. Nesse caso, damos o escopo do singleton para nossa instânciaBrand para que todas as instâncias de carro compartilhem o mesmo objeto de marca.
4.2. Componente
Continuando, vamos criar nossa interface de componente. . Esta é a classe que irá gerar instâncias de Car, injetando dependências fornecidas porVehiclesModule.
Simplificando, precisamos de uma assinatura de método que retorneCarewe need to mark the class with the @Component annotation:
@Singleton
@Component(modules = VehiclesModule.class)
public interface VehiclesComponent {
Car buildCar();
}
Observe como passamos nossa classe de módulo como um argumento para a anotação@Component. If we didn’t do that, Dagger wouldn’t know how to build the car’s dependencies.
Além disso, como nosso módulo fornece um objeto singleton, devemos dar o mesmo escopo ao nosso componente porqueDagger doesn’t allow for unscoped components to refer to scoped bindings.
4.3. Código do cliente
Finalmente, podemos executarmvn compile para acionar os processadores de anotação e gerar o código do injetor.
Depois disso, encontraremos nossa implementação de componente com o mesmo nome da interface, apenas prefixado com “Dagger“:
@Test
public void givenGeneratedComponent_whenBuildingCar_thenDependenciesInjected() {
VehiclesComponent component = DaggerVehiclesComponent.create();
Car carOne = component.buildCar();
Car carTwo = component.buildCar();
Assert.assertNotNull(carOne);
Assert.assertNotNull(carTwo);
Assert.assertNotNull(carOne.getEngine());
Assert.assertNotNull(carTwo.getEngine());
Assert.assertNotNull(carOne.getBrand());
Assert.assertNotNull(carTwo.getBrand());
Assert.assertNotEquals(carOne.getEngine(), carTwo.getEngine());
Assert.assertEquals(carOne.getBrand(), carTwo.getBrand());
}
5. Spring Analogies
Aqueles familiarizados com o Spring podem ter notado alguns paralelos entre os dois frameworks.
A anotação@Module de Dagger torna o contêiner ciente de uma classe de uma forma muito semelhante a qualquer uma das anotações de estereótipo de Spring (por exemplo,@Service,@Controller ...). Da mesma forma,@Provides e@Component são quase equivalentes a@Bean e@Lookup de Spring, respectivamente.
Spring também tem sua anotação@Scope, correlacionada a@Singleton, embora observe aqui já outra diferença em que Spring assume um escopo singleton por padrão, enquanto Dagger assume o que os desenvolvedores do Spring podem se referir como escopo de protótipo, invocando o método do provedor sempre que uma dependência é necessária.
6. Conclusão
Neste artigo, mostramos como configurar e usar o Dagger 2 com um exemplo básico. Também consideramos as diferenças entre injeção em tempo de execução e injeção em tempo de compilação.
Como sempre, todo o código do artigo está disponívelover on GitHub.