1. 简介
本文将带你快速了解 Dagger 2 —— 一个高性能、轻量级的依赖注入(DI)框架。
Dagger 2 支持 Java 和 Android 平台,但由于其依赖编译时代码生成实现注入,因此在 Android 开发中尤为受欢迎。相比运行时反射注入,它的性能更优,启动更快,是目前 Android 领域主流的 DI 框架之一。
2. 依赖注入回顾
简单回顾一下:依赖注入(DI) 是“控制反转(IoC)”思想的一种具体实现方式,即对象不主动创建依赖,而是由外部容器提供所需依赖。
不同框架实现 DI 的方式不同,主要分为两类:
- ✅ 运行时注入:基于反射机制,如 Spring 框架。使用简单,但运行时性能开销较大。
- ✅ 编译时注入:通过注解处理器在编译阶段生成注入代码,如 Dagger 2。启动快、性能高,但学习成本略高。
⚠️ Dagger 2 属于后者 —— 它在编译期生成大量模板代码,避免了运行时反射,因此效率极高。这也是它在 Android 场景中广受青睐的原因。
3. Maven/Gradle 配置
要在项目中使用 Dagger 2,首先需要引入核心依赖和注解处理器。
Maven 配置
<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>
⚠️ 生成的代码会放在 target/generated-sources/annotations
目录下。部分 IDE(如 IntelliJ IDEA)需要手动将该目录标记为“Generated Sources Root”,否则无法识别生成的 DaggerXXXComponent
类。
Gradle 配置(Android 推荐)
implementation 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
✅ 使用 Android Studio 时,Gradle 会自动处理生成目录,无需额外配置。
配置完成后,就可以开始编写 Dagger 代码了。
4. 实现示例:构建一辆车
我们通过一个简单的“造车”例子来演示 Dagger 2 的使用流程。目标是通过依赖注入组装一辆车,其组件包括发动机(Engine)和品牌(Brand)。
4.1 使用 @Inject
标记依赖
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
}
这样 Dagger 就知道如何创建 Car
实例,并自动传入所需的 Engine
和 Brand
。
4.2 创建 Module:提供依赖
Module 是依赖的“供应方”,负责创建对象实例。
- ✅ 使用
@Module
注解标记类 - ✅ 使用
@Provides
注解标记提供依赖的方法
@Module
public class VehiclesModule {
@Provides
public Engine provideEngine() {
return new Engine();
}
@Provides
@Singleton
public Brand provideBrand() {
return new Brand("Baeldung");
}
}
✅ 关键点:
@Provides
方法名可以自定义,Dagger 通过返回类型匹配依赖。@Singleton
表示该Brand
实例是单例的,所有Car
实例共享同一个Brand
对象。Engine
没有加作用域,默认每次请求都会创建新实例(类似 Spring 的 prototype 模式)。
4.3 创建 Component:注入器接口
Component 是连接 Module 和客户端的桥梁,相当于“装配线”。
- ✅ 使用
@Component
注解标记接口 - ✅ 通过
modules
参数指定依赖的 Module - ✅ 定义方法用于获取注入后的对象
@Singleton
@Component(modules = VehiclesModule.class)
public interface VehiclesComponent {
Car buildCar();
}
⚠️ 注意:
- 必须将
VehiclesModule.class
传入@Component(modules = ...)
,否则 Dagger 无法找到依赖提供方。 - 因为 Module 中定义了
@Singleton
绑定,所以 Component 也必须标注@Singleton
,否则编译报错。Dagger 要求作用域匹配,不能让无作用域的 Component 引用有作用域的绑定。
4.4 客户端使用
编译项目后,Dagger 会自动生成 DaggerVehiclesComponent
类(前缀 Dagger
+ 接口名),这是 Component 的实现类。
@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()); // Engine 非单例
Assert.assertEquals(carOne.getBrand(), carTwo.getBrand()); // Brand 单例
}
✅ 输出验证:
- 两辆车的
Engine
实例不同(每次新建) - 两辆车的
Brand
实例相同(单例共享)
这就是作用域控制的实际效果。
5. 与 Spring 的对比
如果你熟悉 Spring,会发现 Dagger 和 Spring 在概念上有不少对应关系:
Spring | Dagger 2 | 说明 |
---|---|---|
@Service / @Component |
@Module |
声明一个组件类 |
@Bean |
@Provides |
提供一个 Bean 实例 |
@Lookup |
Component 方法 | 获取容器中的对象 |
@Scope / @Singleton |
@Singleton |
控制对象生命周期 |
⚠️ 重要区别:
- 默认作用域不同:Spring 默认是
singleton
,而 Dagger 默认是“无作用域”,即每次请求都创建新实例(类似 prototype)。 - 注入时机不同:Spring 是运行时反射,Dagger 是编译时生成代码,性能更高。
简单粗暴地说:Dagger 把 Spring 容器在运行时做的事,提前到编译期完成了。
6. 总结
本文通过一个简单的汽车组装示例,介绍了 Dagger 2 的基本使用流程:
- 使用
@Inject
标记构造函数 - 使用
@Module
+@Provides
提供依赖 - 使用
@Component
定义注入接口 - 编译后使用生成的
DaggerXXXComponent
获取实例
Dagger 2 的优势在于高性能和类型安全,适合对启动速度和内存敏感的场景(如 Android App)。虽然初期学习曲线稍陡,但一旦掌握,能有效解耦代码,提升可测试性。
✅ 所有示例代码已托管至 GitHub:
https://github.com/baeldung/tutorials/tree/master/di-modules/dagger