Dagger / Hilt: что это такое, DI и применение

Автор: IT Sectr Опубликовано: 2026-05-03 Время чтения: 9 мин

Dagger — это фреймворк внедрения зависимостей для Java и Kotlin, генерирующий DI-код на этапе компиляции через обработку аннотаций. Hilt — надстройка над Dagger для Android, упрощающая настройку компонентов и жизненного цикла. По данным Google, 2025, Hilt используется более чем в 70% Android-приложений из Google Play Top-100, поддерживая Activity, Fragment, ViewModel и Service через предопределённые компоненты. Оба фреймворка обеспечивают compile-time проверку графа зависимостей, исключая runtime-ошибки внедрения.

Главное

  • Dagger — compile-time DI фреймворк с генерацией кода через аннотации @Module, @Provides, @Component
  • Hilt — надстройка для Android, упрощающая Dagger через @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel
  • Component — граф зависимостей, связывающий Module с Inject-целями через прокси-методы
  • Scope — @Singleton, @ViewModelScoped, @ActivityScoped управляют временем жизни внедряемых объектов
  • Hilt поддерживает многомодульные проекты через @InstallIn для изолированных графов зависимостей

Что такое Dagger / Hilt?

Dagger — это фреймворк внедрения зависимостей с генерацией кода на этапе компиляции (compile-time DI). Разработанный в Square и позже переданный Google, Dagger использует процессор аннотаций Java APT для анализа графа зависимостей и генерации фабричных классов. В отличие от runtime-DI (Guice, Koin), Dagger не использует рефлексию — весь код создаётся при компиляции, что обеспечивает максимальную производительность в рантайме и выявление ошибок на этапе сборки.

Hilt — это библиотека от Google, построенная поверх Dagger и оптимизированная для Android. Hilt предоставляет предопределённые компоненты, соответствующие жизненному циклу Android-компонентов: @SingletonComponent для Application, @ActivityComponent для Activity, @FragmentComponent для Fragment, @ViewModelComponent для ViewModel. Это устраняет рутинную настройку Component и Module, которая требуется в чистом Dagger. Hilt также генерирует граф зависимостей для каждого Android-компонента автоматически через @AndroidEntryPoint.

По данным Google I/O 2024, Hilt является рекомендованным решением для DI в Android-приложениях, написанных на Kotlin. Jetpack-библиотеки (Navigation, Room, WorkManager) имеют встроенную интеграцию с Hilt через @HiltViewModel и @HiltWorker. В проектах, не использующих Android (чистая Java/Kotlin библиотеки, серверные приложения), применяется чистый Dagger без Hilt-надстройки.

Проблема ручного внедрения зависимостей

Без DI-фреймворка разработчик создаёт объекты вручную через конструкторы или фабрики, передавая зависимости по цепочке. Каждое новое требование — изменение сигнатуры всех конструкторов в цепочке. Dagger автоматизирует этот процесс: достаточно объявить, какой тип требуется (@Inject constructor), и Dagger создаст граф зависимостей, разрешив все вложенные типы. При изменении зависимостей Dagger обновляет сгенерированный код автоматически — ошибиться в цепочке невозможно.

Принципы внедрения зависимостей

Внедрение зависимостей (Dependency Injection) — это паттерн, при котором объект получает свои зависимости извне, а не создаёт их самостоятельно. DI реализует принцип инверсии управления (IoC): класс не отвечает за создание своих зависимостей, а декларирует их через конструктор, метод или поле. Внедрение через конструктор (constructor injection) считается наиболее предпочтительным, так как оно гарантирует, что объект создаётся в валидном состоянии.

Тип внедренияСинтаксис DaggerКогда использовать
Constructor injection@Inject constructorОсновной способ — для всех собственных классов
Field injection@Inject lateinit varТолько для Android-компонентов (Activity, Fragment)
Method injection@Inject fun bind()Для post-construct инициализации

Преимущества compile-time DI

Основные преимущества DI включают тестируемость (зависимости подменяются через mock-объекты), слабую связанность (классы зависят от интерфейсов, а не реализаций), и явное управление жизненным циклом объектов через scopes. Dagger автоматически гарантирует, что объект создаётся единожды в рамках своего скоупа и уничтожается при выходе из скоупа.

Архитектура Dagger: Component, Module, Provides

Component — центральный элемент графа зависимостей Dagger. Это интерфейс, аннотированный @Component, который описывает мост между Module и целями внедрения. Dagger генерирует реализацию Component (например, DaggerAppComponent) на этапе компиляции. Component определяет, какие типы доступны для внедрения, через абстрактные методы, возвращающие нужные типы, или через методы inject, принимающие объект для field injection.

kotlin
// Module: предоставляет зависимости, которые Dagger не может создать сам
@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideApiService(client: OkHttpClient): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Component: связывает Module и Injection-цели
@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
    fun getApiService(): ApiService
}

@Module — это класс, содержащий методы с @Provides, которые возвращают экземпляры зависимостей. Module используется для типов, которые Dagger не может создать автоматически: сторонние библиотеки (OkHttp, Retrofit), объекты с параметрами конструктора, интерфейсы с выбором реализации. @Binds — альтернатива @Provides для случаев, когда метод возвращает интерфейс и принимает единственную реализацию: Dagger генерирует прямой cast без вызова метода.

@Scope определяет время жизни объекта в графе зависимостей. @Singleton — объект создаётся один раз на всё приложение. @ActivityScoped — объект живёт, пока жива Activity. @FragmentScoped — пока жив Fragment. Без скоупа Dagger создаёт новый экземпляр при каждом внедрении. @Reusable — скоуп для объектов, которые не обязаны быть синглтонами, но их создание дорого — Dagger может кэшировать экземпляр, но не гарантирует этого.

Hilt для Android: @HiltAndroidApp и @AndroidEntryPoint

Hilt упрощает настройку Dagger для Android за счёт предопределённых компонентов и автоматической генерации базового графа. Аннотация @HiltAndroidApp на Application-классе запускает генерацию Hilt-компонента. Без этой аннотации Hilt не работает — она обязательна для любого Android-приложения, использующего Hilt. @HiltAndroidApp создаёт родительский компонент SingletonComponent, от которого наследуются все остальные компоненты приложения.

kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // apiService уже внедрён до вызова onCreate
    }
}

@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext ctx: Context): AppDatabase {
        return Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
    }
}

@AndroidEntryPoint — аннотация для Activity, Fragment, Service, BroadcastReceiver и View. Она генерирует Hilt-компонент для каждого типа: @AndroidEntryPoint на Activity создаёт ActivityComponent, который наследуется от SingletonComponent. Дочерний компонент автоматически получает все зависимости родителя. Field injection с @Inject lateinit var доступен только в классах, аннотированных @AndroidEntryPoint — в обычных классах используется constructor injection.

@InstallIn указывает, в какой Hilt-компонент устанавливается модуль. NetworkModule с @InstallIn(SingletonComponent::class) доступен во всём приложении. Module с @InstallIn(ActivityComponent::class) — только в Activity. Это изолирует графы зависимостей: Activity-специфичные модули не видны в Fragment и ViewModel, предотвращая случайное использование недопустимых зависимостей. @ApplicationContext — встроенный квалификатор Hilt для получения Context приложения.

Qualifier: @Named и кастомные квалификаторы

Когда требуется внедрить две разные реализации одного интерфейса, используются квалификаторы. Hilt поддерживает @Named для строковых идентификаторов и кастомные аннотации с @Qualifier. Например, @Named("baseUrl") и @Named("imageBaseUrl") для разных строковых конфигураций. Кастомные квалификаторы предпочтительнее @Named из-за проверки на этапе компиляции — неправильное имя строки не будет обнаружено до рантайма.

Hilt ViewModel: @HiltViewModel и @Inject constructor

@HiltViewModel — аннотация, заменяющая ручную фабрику ViewModelProvider.Factory. Класс, аннотированный @HiltViewModel с @Inject constructor, автоматически получает все зависимости через Dagger. Hilt генерирует ViewModelFactory, используемую Jetpack ViewModelProvider. Без Hilt разработчику приходится писать фабрику вручную, передавая каждый параметр из Activity или фрагмента.

kotlin
@HiltViewModel
class MainViewModel
    @Inject constructor(
        private val apiService: ApiService,
        private val database: AppDatabase
    ) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    fun loadUsers() {
        viewModelScope.launch {
            _users.value = apiService.getUsers()
        }
    }
}

// В Activity — Hilt автоматически создаёт ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

ViewModelScoped — скоуп Hilt для зависимостей, которые живут, пока жив ViewModel. Если два ViewModel одного типа внедряют одну @ViewModelScoped-зависимость, каждая получает свой экземпляр. Это отличает @ViewModelScoped от @ActivityScoped, где одна Activity получает один экземпляр для всех фрагментов. Для ViewModel-специфичных зависимостей (например, SavedStateHandle) используется @HiltViewModel с @Inject constructor(savedStateHandle: SavedStateHandle).

Hilt поддерживает assisted injection через библиотеку Hilt Extensions. Assisted injection позволяет передавать параметры в конструктор во время внедрения, когда часть зависимостей известна только в рантайме (например, ID пользователя из интента). Для assisted injection используется @AssistedInject в комбинации с @Assisted-параметрами. Hilt генерирует AssistedFactory, которую можно внедрить стандартным способом.

Dagger vs Hilt: сравнение и миграция

Чистый Dagger требует ручного создания Component, определения скоупов и настройки инъекции в каждый Android-компонент. Разработчик создаёт AppComponent, ActivityComponent, FragmentComponent, управляет их связями через @Subcomponent. Этот подход даёт максимальный контроль, но требует значительного boilerplate-кода. Dagger используется в крупных проектах, где требуется нестандартная архитектура DI, или в не-Android Java/Kotlin-проектах.

Hilt автоматизирует boilerplate: один @HiltAndroidApp, один @AndroidEntryPoint для каждого компонента, предопределённые скоупы. Hilt рекомендуется Google для всех новых Android-проектов. Миграция с Dagger на Hilt включает замену Component на @InstallIn, замену @Subcomponent на предопределённые Hilt-компоненты и замену ручного ViewModelProvider.Factory на @HiltViewModel. Большинство @Module-классов переносятся с добавлением @InstallIn без изменения @Provides методов.

ХарактеристикаDaggerHilt
НастройкаРучная: Component, Subcomponent, BuilderАвто: @HiltAndroidApp, @AndroidEntryPoint
Android-компонентыНет предопределённых12+ встроенных компонентов
ViewModelРучная фабрика@HiltViewModel + @Inject constructor
MultimoduleЧерез @Component(dependencies)Через @InstallIn + агрегацию
СложностьВысокая — нужен опытНизкая — интуитивно понятный
ГибкостьМаксимальнаяСтандартная (покрывает 95% сценариев)

Ограничения Hilt: библиотека поддерживает только Android (не подходит для чисто серверных Java-проектов), накладывает определённую структуру компонентов (сложно переопределить), и добавляет зависимость от android.hilt:hilt-navigation-compose для Jetpack Compose-проектов. Для Compose-приложений Hilt предоставляет @HiltViewModel, доступный в Composable через hiltViewModel() — без ручного предоставления ViewModel из Activity.

Часто задаваемые вопросы

В чём разница между Dagger и Hilt?

Dagger — базовый compile-time DI фреймворк с ручной настройкой Component и Module. Hilt — надстройка для Android, автоматизирующая создание компонентов и интеграцию с жизненным циклом Activity, Fragment, ViewModel, Service и BroadcastReceiver.

Зачем нужен @HiltAndroidApp?

@HiltAndroidApp включает генерацию Hilt-компонента для Application. Без этой аннотации Hilt не может создать базовый компонент SingletonComponent, от которого наследуются все ActivityComponent, FragmentComponent и ViewModelComponent. Аннотация обязательна для любого Hilt-проекта.

Как Hilt работает с Jetpack Navigation?

Hilt Navigation предоставляет @HiltViewModel для ViewModel в NavBackStackEntry и hiltNavGraphViewModels() для скоупинга ViewModel в рамках навигационного графа. Библиотека android.hilt:hilt-navigation-fragment автоматически создаёт ViewModel для каждого NavBackStackEntry.

Как внедрить Context в Hilt?

Используйте @ApplicationContext для контекста приложения или @ActivityContext для контекста Activity. Hilt предоставляет эти квалификаторы встроенными в библиотеку android.hilt:hilt-android. @ActivityContext доступен только в модулях, установленных в ActivityComponent.

Что такое @Binds и когда его использовать?

@Binds — эффективная альтернатива @Provides, когда метод принимает ровно один параметр и возвращает его тип как интерфейс. @Binds генерирует прямой cast без вызова метода, что уменьшает количество сгенерированного кода и повышает производительность при внедрении.

Итоги

  • Dagger — compile-time DI фреймворк с аннотациями @Module, @Provides, @Component и генерацией кода через APT
  • Hilt — Android-надстройка над Dagger с @HiltAndroidApp, @AndroidEntryPoint, @InstallIn и предопределёнными компонентами
  • Component управляет графом зависимостей, Module предоставляет сторонние классы, Provides — фабрики объектов
  • Scope (@Singleton, @ViewModelScoped, @ActivityScoped) определяет время жизни объекта в графе Dagger
  • @HiltViewModel автоматизирует создание ViewModel, устраняя ручные фабрики ViewModelProvider.Factory
  • @InstallIn изолирует модули по компонентам, предотвращая утечку зависимостей между слоями приложения
  • Hilt рекомендуется Google для всех новых Android-проектов, Dagger — для не-Android и кастомных DI-архитектур

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также