← Назад к вопросам

Как работает Dagger?

2.0 Middle🔥 201 комментариев
#Dependency Injection#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает Dagger

Что такое Dagger

Dagger — фреймворк для Dependency Injection (внедрения зависимостей) в Java/Kotlin. Он генерирует код на этапе компиляции, что делает его невероятно быстрым на runtime (в отличие от reflection-based DI фреймворков).

Для Android используется Dagger 2 (или Hilt — обертка над Dagger 2).

Основные концепции

1. Component

Component — это контейнер, который знает, как создавать экземпляры зависимостей.

@Component(modules = [AppModule::class])
interface AppComponent {
  fun inject(activity: MainActivity)
}

Это интерфейс, и Dagger сгенерирует реализацию (DaggerAppComponent).

2. Module

Module — это класс, содержащий провайдеры для создания зависимостей.

@Module
class AppModule {
  @Provides
  @Singleton
  fun provideRepository(): UserRepository {
    return UserRepository()
  }
  
  @Provides
  fun provideService(repository: UserRepository): UserService {
    return UserService(repository)
  }
}

Dagger сканирует методы с @Provides и автоматически создает граф зависимостей.

3. Scope

Scope определяет жизненный цикл объекта:

  • @Singleton — один экземпляр на все приложение
  • @ActivityScope — один экземпляр на Activity
  • Без аннотации — новый экземпляр каждый раз
@Module
class DatabaseModule {
  @Provides
  @Singleton  // Один Database на все приложение
  fun provideDatabase(): AppDatabase {
    return Room.databaseBuilder(context, AppDatabase::class.java, "db").build()
  }
}

Как это работает шаг за шагом

Шаг 1: Определяешь зависимости

class UserRepository(val apiClient: ApiClient)

class UserService(val repository: UserRepository)

Шаг 2: Создаешь Module с провайдерами

@Module
class AppModule {
  @Provides
  @Singleton
  fun provideApiClient(): ApiClient = Retrofit.Builder()
    .baseUrl("https://api.example.com")
    .build()
    .create(ApiClient::class.java)
  
  @Provides
  @Singleton
  fun provideRepository(apiClient: ApiClient): UserRepository {
    return UserRepository(apiClient)
  }
  
  @Provides
  fun provideService(repository: UserRepository): UserService {
    return UserService(repository)
  }
}

Шаг 3: Создаешь Component

@Component(modules = [AppModule::class])
interface AppComponent {
  fun inject(activity: MainActivity)
}

Шаг 4: Dagger генерирует код

После компиляции Dagger создает класс DaggerAppComponent, который знает, как собрать граф:

// Сгенерировано Dagger'ом
class DaggerAppComponent : AppComponent {
  private val apiClient: ApiClient = provideApiClient()
  private val repository: UserRepository = provideRepository(apiClient)
  
  override fun inject(activity: MainActivity) {
    activity.service = provideService(repository)
  }
}

Шаг 5: Используешь в коде

class MainActivity : AppCompatActivity() {
  @Inject
  lateinit var userService: UserService
  
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    val component = DaggerAppComponent.create()
    component.inject(this)
    
    // userService готов к использованию!
    userService.loadUsers()
  }
}

Субкомпоненты для Scoping

@ActivityScope
@Subcomponent(modules = [ActivityModule::class])
interface ActivityComponent {
  fun inject(activity: MainActivity)
}

@Module(subcomponents = [ActivityComponent::class])
class AppModule

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    val activityComponent = (app.appComponent as AppComponent)
      .activityComponent()
      .create()
    
    activityComponent.inject(this)
  }
}

Dagger Hilt (рекомендуется для Android)

Hilt — обертка над Dagger 2, которая автоматизирует много боллерплейта:

@Module
@InstallIn(SingletonComponent::class)  // Вместо @Component
class AppModule {
  @Provides
  @Singleton
  fun provideRepository(): UserRepository = UserRepository()
}

@HiltAndroidApp  // В Application класс
class MyApp : Application()

@AndroidEntryPoint  // В Activity/Fragment
class MainActivity : AppCompatActivity() {
  @Inject
  lateinit var repository: UserRepository
}

Преимущества Dagger

Компиляция вместо runtime — код генерируется при компиляции, нет reflection
Граф зависимостей проверяется на компиляции — ошибки не упустишь
Высокая производительность — нет reflection overhead
Явность — все зависимости видны в коде
Scoping — полный контроль над жизненным циклом объектов

Недостатки

Боллерплейт — много кода для маленьких проектов
Кривая обучения — сложный концептуально
Медленная компиляция — генерация кода замедляет сборку

Практический пример: приложение с Database

@Module
@InstallIn(SingletonComponent::class)
class DatabaseModule {
  @Provides
  @Singleton
  fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
    return Room.databaseBuilder(context, AppDatabase::class.java, "app_db")
      .build()
  }
  
  @Provides
  fun provideUserDao(db: AppDatabase): UserDao = db.userDao()
}

@Module
@InstallIn(SingletonComponent::class)
class RepositoryModule {
  @Provides
  fun provideUserRepository(dao: UserDao): UserRepository {
    return UserRepository(dao)
  }
}

@HiltAndroidApp
class MyApp : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
  @Inject
  lateinit var repository: UserRepository
  
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    repository.getUsers()  // Готов!
  }
}

Итог

Dagger работает в три этапа:

  1. Компиляция — сканирует @Module/@Provides/Component
  2. Генерация — создает граф зависимостей и классы (DaggerXxx)
  3. Runtime — просто вызывает сгенерированный код, нет reflection

Для современного Android используй Hilt — это официально рекомендуемый подход, который убирает половину боллерплейта.