Dagger2 依赖注入深度解析:编译时注解处理与依赖图生成
Dagger2 是 Google 推出的编译时依赖注入框架,相比运行时反射的注入方案,它通过 APT(Annotation Processing Tool)在编译期生成代码,避免了反射带来的性能开销。本文从注解处理器、依赖图生成、模块化设计三个层面,深入解析 Dagger2 的核心原理。
为什么需要依赖注入
传统依赖管理的问题
kotlin
class UserRepository(private val api: ApiService) {
suspend fun getUser(id: String) = api.fetchUser(id)
}
class UserViewModel {
private val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
private val api = retrofit.create(ApiService::class.java)
private val repository = UserRepository(api)
fun loadUser(id: String) = repository.getUser(id)
}
痛点:
- 强耦合:ViewModel 直接创建 Retrofit 实例,无法替换实现
- 难测试:无法注入 Mock 对象进行单元测试
- 生命周期混乱:每个 ViewModel 都创建新的 Retrofit 实例
Dagger2 的解决方案
kotlin
@Module
class NetworkModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
@Provides
fun provideApiService(retrofit: Retrofit): ApiService =
retrofit.create(ApiService::class.java)
}
class UserViewModel @Inject constructor(
private val repository: UserRepository
) {
fun loadUser(id: String) = repository.getUser(id)
}
优势:
- 解耦:ViewModel 只声明需要什么,不关心怎么创建
- 可测试:通过 Module 替换为 Mock 实现
- 单例管理 :
@Singleton确保全局唯一
Dagger2 核心原理
1. APT 注解处理器
Dagger2 通过 dagger-compiler 在编译期扫描注解,生成工厂类和注入器:
kotlin
// 源代码
@Module
class AppModule {
@Provides
fun provideContext(app: Application): Context = app.applicationContext
}
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
}
编译后生成的代码(简化版):
java
// DaggerAppComponent.java
public final class DaggerAppComponent implements AppComponent {
private Provider<Context> contextProvider;
private DaggerAppComponent(Application app) {
this.contextProvider = AppModule_ProvideContextFactory.create(
new AppModule(), app
);
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private void injectMainActivity(MainActivity activity) {
MainActivity_MembersInjector.injectRepository(
activity, repositoryProvider.get()
);
}
}
关键点:
- 生成的
DaggerAppComponent实现了AppComponent接口 Provider<T>是工厂模式的封装,按需创建对象MembersInjector负责将依赖注入到目标类的字段
2. 依赖图生成
Dagger2 在编译期构建依赖图(DAG),检查循环依赖和缺失依赖:
kotlin
@Module
class DataModule {
@Provides
fun provideDatabase(): AppDatabase = Room.databaseBuilder(...)
@Provides
fun provideUserDao(db: AppDatabase): UserDao = db.userDao()
}
class UserRepository @Inject constructor(
private val dao: UserDao,
private val api: ApiService
)
依赖关系图:
csharp
AppComponent
└─ UserRepository
├─ UserDao
│ └─ AppDatabase (from DataModule)
└─ ApiService (from NetworkModule)
编译期检查:
- 如果
AppDatabase没有被@Provides提供,编译失败 - 如果存在
A -> B -> C -> A的循环依赖,报错
3. Scope 作用域管理
Dagger2 通过 Scope 注解管理对象生命周期:
kotlin
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
fun provideUserRepository(): UserRepository
}
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ActivityScope
@ActivityScope
@Subcomponent
interface ActivityComponent {
fun provideViewModel(): UserViewModel
}
生命周期映射:
@Singleton→ 跟随 AppComponent 生命周期(全局单例)@ActivityScope→ 跟随 ActivityComponent 生命周期(Activity 级单例)
实现原理:
java
// 生成的单例缓存
public final class DaggerAppComponent {
private volatile Provider<UserRepository> repositoryProvider;
private UserRepository getUserRepository() {
if (repositoryProvider == null) {
synchronized (this) {
if (repositoryProvider == null) {
repositoryProvider = DoubleCheck.provider(
new UserRepository_Factory(daoProvider, apiProvider)
);
}
}
}
return repositoryProvider.get();
}
}
关键 :DoubleCheck.provider() 实现了双重检查锁,确保线程安全的单例。
实战:多模块依赖注入
场景:App + Feature 模块
kotlin
// :app module
@Singleton
@Component(modules = [AppModule::class, NetworkModule::class])
interface AppComponent {
fun userComponent(): UserComponent.Factory
}
// :feature-user module
@Subcomponent(modules = [UserModule::class])
@ActivityScope
interface UserComponent {
fun inject(activity: UserActivity)
@Subcomponent.Factory
interface Factory {
fun create(): UserComponent
}
}
@Module
class UserModule {
@Provides
@ActivityScope
fun provideViewModel(repository: UserRepository): UserViewModel =
UserViewModel(repository)
}
注入流程
- App 启动时创建 AppComponent
kotlin
class MyApplication : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
}
- Activity 创建 UserComponent
kotlin
class UserActivity : AppCompatActivity() {
@Inject lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
(application as MyApplication).appComponent
.userComponent()
.create()
.inject(this)
super.onCreate(savedInstanceState)
}
}
依赖关系:
UserViewModel需要UserRepositoryUserRepository由AppComponent提供(跨模块共享)UserViewModel由UserComponent提供(Activity 级)
进阶技巧
1. Lazy 注入延迟初始化
kotlin
class MainActivity : AppCompatActivity() {
@Inject lateinit var database: Lazy<AppDatabase>
override fun onCreate(savedInstanceState: Bundle?) {
component.inject(this)
super.onCreate(savedInstanceState)
// 只有在第一次调用 get() 时才创建数据库
val db = database.get()
}
}
2. Provider 多次获取新实例
kotlin
class ImageLoader @Inject constructor(
private val bitmapProvider: Provider<Bitmap>
) {
fun loadMultipleImages() {
val bitmap1 = bitmapProvider.get() // 新实例
val bitmap2 = bitmapProvider.get() // 另一个新实例
}
}
3. Multibindings 集合注入
kotlin
@Module
interface PluginModule {
@Binds
@IntoSet
fun bindGlidePlugin(impl: GlidePluginImpl): Plugin
@Binds
@IntoSet
fun bindOkHttpPlugin(impl: OkHttpPluginImpl): Plugin
}
class PluginManager @Inject constructor(
private val plugins: Set<@JvmSuppressWildcards Plugin>
) {
fun initialize() {
plugins.forEach { it.init() }
}
}
与 Hilt 的对比
| 维度 | Dagger2 | Hilt |
|---|---|---|
| 配置复杂度 | 需要手动定义 Component | 自动生成 Component |
| Android 集成 | 需要手动注入 | 支持 @AndroidEntryPoint |
| ViewModel 注入 | 需要自定义 Factory | 原生支持 @HiltViewModel |
| 适用场景 | 纯 Kotlin/Java 项目 | Android 项目优先 |
Hilt 本质是 Dagger2 的上层封装,底层仍然使用 Dagger2 的 APT 生成代码。
常见问题排查
1. 编译报错:Missing binding
go
error: [Dagger/MissingBinding] ApiService cannot be provided
原因 :ApiService 没有被 @Provides 或 @Inject 标记。
解决:
kotlin
@Module
class NetworkModule {
@Provides
fun provideApiService(retrofit: Retrofit): ApiService =
retrofit.create(ApiService::class.java)
}
2. 循环依赖
rust
error: [Dagger/DependencyCycle] Found a dependency cycle:
A -> B -> C -> A
解决方案:
- 使用
Lazy<T>打破循环 - 重新设计依赖关系
3. Scope 不匹配
kotlin
@Singleton
class AppRepository
@ActivityScope
class UserViewModel @Inject constructor(
private val repository: AppRepository // ❌ Scope 不匹配
)
规则:子 Component 可以依赖父 Component 的 Singleton 对象,但不能反向依赖。
总结
Dagger2 通过编译期代码生成实现依赖注入,核心优势在于:
- 零运行时开销:APT 生成的代码直接调用构造函数,无反射
- 编译期检查:依赖缺失和循环依赖在编译期就能发现
- 作用域管理:Scope 注解清晰定义对象生命周期
对于 Android 项目,建议优先使用 Hilt,享受自动化配置和 Android 组件集成的便利;对于纯 Kotlin/Java 项目或需要精细控制的场景,Dagger2 仍然是最佳选择。
理解 Dagger2 的原理,也是掌握现代 Android 架构设计的重要一环。