1. Introduction
1.介绍
In this tutorial, we’ll take a look at Dagger 2 – a fast and lightweight dependency injection framework.
在本教程中,我们将看看Dagger 2 – 一个快速和轻量级的依赖注入框架。
The framework is available for both Java and Android, but the high-performance derived from compile-time injection makes it a leading solution for the latter.
该框架同时适用于Java和Android,但从编译时注入衍生的高性能使其成为后者的领先解决方案。
2. Dependency Injection
2.依赖性注入
As a bit of a reminder, Dependency Injection is a concrete application of the more generic Inversion of Control principle in which the flow of the program is controlled by the program itself.
作为一点提醒,依赖注入是更通用的反转控制原则的具体应用,其中程序的流程是由程序本身控制的。
It’s implemented through an external component which provides instances of objects (or dependencies) needed by other objects.
它是通过一个外部组件实现的,该组件提供其他对象所需要的对象(或依赖)的实例。
And different frameworks implement dependency injection in different ways. In particular, one of the most notable of these differences is whether the injection happens at run-time or at compile-time.
而不同的框架以不同的方式实现依赖注入。特别是,这些差异中最明显的一点是,注入是在运行时还是在编译时发生。
Run-time DI is usually based on reflection which is simpler to use but slower at run-time. An example of a run-time DI framework is Spring.
运行时DI通常基于反射,使用起来比较简单,但在运行时比较慢。运行时DI框架的一个例子是Spring。
Compile-time DI, on the other hand, is based on code generation. This means that all the heavy-weight operations are performed during compilation. Compile-time DI adds complexity but generally performs faster.
另一方面,编译时的DI是基于代码生成的。这意味着所有重量级的操作都在编译过程中进行。编译时DI增加了复杂性,但通常执行得更快。
Dagger 2 falls into this category.
小刀2》就属于这一类。
3. Maven/Gradle Configuration
3.Maven/Gradle配置
In order to use Dagger in a project, we’ll need to add the dagger dependency to our pom.xml:
为了在项目中使用Dagger,我们需要在我们的pom.xml中加入dagger依赖项。
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>2.16</version>
</dependency>
Furthermore, we’ll also need to include the Dagger compiler used to convert our annotated classes into the code used for the injections:
此外,我们还需要包括Dagger编译器,用于将我们的注释类转换为用于注入的代码。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.16</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
With this configuration, Maven will output the generated code into target/generated-sources/annotations.
使用该配置,Maven会将生成的代码输出到target/generated-sources/annotations。
For this reason, we likely need to further configure our IDE if we want to use any of its code completion features. Some IDEs have direct support for annotation processors while others may need us to add this directory to the build path.
由于这个原因,如果我们想使用IDE的任何代码完成功能,我们可能需要进一步配置我们的IDE。一些IDE直接支持注释处理器,而其他IDE可能需要我们将这个目录添加到构建路径中。
Alternatively, if we’re using Android with Gradle, we can include both dependencies:
另外,如果我们使用Android与Gradle,我们可以包括这两个依赖。
compile 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
Now that we have Dagger in our project, let’s create a sample application to see how it works.
现在我们的项目中已经有了Dagger,让我们创建一个示例应用程序来看看它是如何工作的。
4. Implementation
4.实施
For our example, we’ll try to build a car by injecting its components.
在我们的例子中,我们将尝试通过注入组件来建造一辆汽车。
Now, Dagger uses the standard JSR-330 annotations in many places, one being @Inject.
现在,Dagger在很多地方使用了标准的JSR-330注解,其中之一就是@Inject.。
We can add the annotations to fields or the constructor. But, since Dagger doesn’t support injection on private fields, we’ll go for constructor injection to preserve encapsulation:
我们可以在字段或构造函数中添加注解。但是,由于Dagger不支持对私有字段的注入,我们将选择构造函数的注入以保护封装。
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
}
Next, we’ll implement the code to perform the injection. More specifically, we’ll create:
接下来,我们将实现执行注入的代码。更具体地说,我们将创建。
- a module, which is a class that provides or builds the objects’ dependencies, and
- a component, which is an interface used to generate the injector
Complex projects may contain multiple modules and components but since we’re dealing with a very basic program, one of each is enough.
复杂的项目可能包含多个模块和组件,但由于我们处理的是一个非常基本的程序,每一种都有一个就足够了。
Let’s see how to implement them.
让我们看看如何实施它们。
4.1. Module
4.1.模块[/strong]
To create a module, we need to annotate the class with the @Module annotation. This annotation indicates that the class can make dependencies available to the container:
为了创建一个模块,我们需要用@Module annotation来注释这个类。这个注解表明,该类可以向容器提供依赖关系。
@Module
public class VehiclesModule {
}
Then, we need to add the @Provides annotation on methods that construct our dependencies:
然后,我们需要在构建我们的依赖关系的方法上添加@Provides注解。
@Module
public class VehiclesModule {
@Provides
public Engine provideEngine() {
return new Engine();
}
@Provides
@Singleton
public Brand provideBrand() {
return new Brand("Baeldung");
}
}
Also, note that we can configure the scope of a given dependency. In this case, we give the singleton scope to our Brand instance so all the car instances share the same brand object.
另外,请注意,我们可以配置一个给定的依赖关系的范围。在这种情况下,我们给我们的Brand实例以单子范围,所以所有的汽车实例都共享同一个品牌对象。
4.2. Component
4.2.组成部分
Moving on, we’re going to create our component interface. This is the class that will generate Car instances, injecting dependencies provided by VehiclesModule.
接着,我们将创建我们的组件接口。这是一个将生成汽车实例的类,注入由VehiclesModule提供的依赖项。
Simply put, we need a method signature that returns a Car and we need to mark the class with the @Component annotation:
简单地说,我们需要一个返回汽车的方法签名,并且我们需要用@Component注解标记这个类。
@Singleton
@Component(modules = VehiclesModule.class)
public interface VehiclesComponent {
Car buildCar();
}
Notice how we passed our module class as an argument to the @Component annotation. If we didn’t do that, Dagger wouldn’t know how to build the car’s dependencies.
注意我们是如何将我们的模块类作为参数传递给@Component注解的。如果我们不这样做,Dagger就不知道如何构建汽车的依赖关系。
Also, since our module provides a singleton object, we must give the same scope to our component because Dagger doesn’t allow for unscoped components to refer to scoped bindings.
另外,由于我们的模块提供了一个单子对象,我们必须给我们的组件提供同样的范围,因为Dagger不允许未范围的组件引用范围的绑定。
4.3. Client Code
4.3.客户端代码
Finally, we can run mvn compile in order to trigger the annotation processors and generate the injector code.
最后,我们可以运行mvn compile,以触发注释处理器并生成注入器代码。
After that, we’ll find our component implementation with the same name as the interface, just prefixed with “Dagger“:
之后,我们会发现我们的组件实现与接口的名称相同,只是前缀为”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
5 Spring的比喻
Those familiar with Spring may have noticed some parallels between the two frameworks.
熟悉Spring的人可能已经注意到这两个框架之间的一些相似之处。
Dagger’s @Module annotation makes the container aware of a class in a very similar fashion as any of Spring’s stereotype annotations (for example, @Service, @Controller…). Likewise, @Provides and @Component are almost equivalent to Spring’s @Bean and @Lookup respectively.
Dagger的@Module注解使容器意识到一个类,其方式与Spring的任何定型注解非常相似(例如,@Service,@Controller)。同样地,@Provides和@Component几乎等同于Spring的@Bean和@Lookup。
Spring also has its @Scope annotation, correlating to @Singleton, though note here already another difference in that Spring assumes a singleton scope by default while Dagger defaults to what Spring developers might refer to as the prototype scope, invoking the provider method each time a dependency is required.
Spring也有它的@Scope注解,与@Singleton相关,不过请注意这里已经有了另一个区别,Spring默认是一个单子范围,而Dagger默认是Spring开发者所说的原型范围,每次需要依赖时都会调用提供者方法。
6. Conclusion
6.结论
In this article, we went through how to set up and use Dagger 2 with a basic example. We also considered the differences between run-time and compile-time injection.
在这篇文章中,我们通过一个基本的例子,了解了如何设置和使用Dagger 2。我们还考虑了运行时和编译时注入的区别。
As always, all the code in the article is available over on GitHub.
一如既往,文章中的所有代码都可以在GitHub上找到。