1. 简介
在本教程中,我们将介绍Dagger 2-一个快速、轻量级的依赖注入框架。
该框架适用于Java和Android,但编译时注入带来的高性能使其成为后者的领先解决方案。
2. 依赖注入
提醒一下,依赖注入是更通用的控制反转原则的具体应用,其中程序流程由程序本身控制。
它是通过外部组件实现的,该组件提供其他对象所需的对象(或依赖)的实例。
不同的框架实现依赖注入的方式也不同。其中最显著的区别之一就是注入是在运行时还是编译时进行。
运行时DI通常基于反射,它使用起来更简单,但运行时速度较慢,Spring就是一个运行时DI框架的示例。
另一方面,编译时DI基于代码生成,这意味着所有重量级操作都在编译期间执行,编译时DI增加了复杂性,但通常执行速度更快。
Dagger 2就属于这一类。
3. Maven/Gradle配置
为了在项目中使用Dagger,我们需要将dagger依赖添加到pom.xml中:
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>2.16</version>
</dependency>
此外,我们还需要包含用于将带注解的类转换为用于注入的代码的Dagger编译器:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.16</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
通过此配置,Maven将把生成的代码输出到target/generated-sources/annotations中。
因此,如果我们想使用IDE的代码补全功能,可能需要进一步配置它。有些IDE直接支持注解处理器,而有些IDE则可能需要我们将此目录添加到构建路径中。
或者,如果我们使用带有Gradle的Android,我们可以同时包含这两个依赖:
compile 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
现在我们的项目中有了Dagger,让我们创建一个示例应用程序来查看它是如何工作的。
4. 实现
为了举例,我们将尝试通过注入零部件来制造一辆汽车。
现在,Dagger在很多地方使用标准JSR-330注解,其中之一就是@Inject。
我们可以将注解添加到字段或构造函数中,但是,由于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
}
接下来,我们将实现执行注入的代码,更具体地说,我们将创建:
- 模块,它是一个提供或构建对象依赖的类
- 组件,用于生成注入器的接口
复杂的项目可能包含多个模块和组件,但由于我们处理的是一个非常基本的程序,因此每个模块和组件一个就足够了。
让我们看看如何实现它们。
4.1 模块
要创建模块,我们需要使用@Module注解来标注该类,此注解表明该类可以将依赖提供给容器:
@Module
public class VehiclesModule {
}
然后,我们需要在构建依赖的方法上添加@Provides注解:
@Module
public class VehiclesModule {
@Provides
public Engine provideEngine() {
return new Engine();
}
@Provides
@Singleton
public Brand provideBrand() {
return new Brand("Tuyucheng");
}
}
另外,请注意,我们可以配置给定依赖的作用域。在本例中,我们为Brand实例赋予了单例作用域,以便所有汽车实例共享同一个品牌对象。
4.2 组件
接下来,我们将创建组件接口,该类将生成Car实例,并注入由VehiclesModule提供的依赖。
简而言之,我们需要一个返回Car的方法签名,并且需要用@Component注解标记该类:
@Singleton
@Component(modules = VehiclesModule.class)
public interface VehiclesComponent {
Car buildCar();
}
注意我们如何将模块类作为参数传递给@Component注解,如果不这样做,Dagger就不知道如何构建汽车的依赖。
此外,由于我们的模块提供了一个单例对象,我们必须为我们的组件提供相同的范围,因为Dagger不允许无范围的组件引用有范围的绑定。
4.3 客户端代码
最后,我们可以运行mvn compile来触发注解处理器并生成注入器代码。
之后,我们会发现我们的组件实现与接口同名,只是前缀为“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类比
熟悉Spring的人可能已经注意到这两个框架之间的一些相似之处。
Dagger的@Module注解使容器能够感知类,其方式与Spring的其他构造型注解(例如@Service、@Controller等)非常相似。同样,@Provides和@Component几乎分别相当于Spring的@Bean和@Lookup。
Spring也有其自己的@Scope注解,与@Singleton相关,但请注意这里已经存在的另一个区别是Spring默认采用单例范围,而Dagger默认采用Spring开发人员可能称为原型范围的范围,每次需要依赖时都会调用提供程序方法。
6. 总结
在本文中,我们通过一个基本示例介绍了如何设置和使用Dagger 2,我们还探讨了运行时注入和编译时注入之间的区别。
Post Directory
