Изучение
Начало работы
Конфигурация, разрешение и закрытие небольшого контейнера Ark DI.
Начало работы
На этой странице создаётся небольшой граф объектов с тремя разными временами
жизни. Важно не только получить CatalogService, но и понимать, какой scope
создаёт, удерживает и закрывает каждый объект.
1. Добавьте пакет
dart pub add ark_di
import 'package:ark_di/ark_di.dart';
2. Не связывайте прикладные объекты с контейнером
Зависимости остаются обычными параметрами конструкторов:
final class AppConfig {
const AppConfig(this.apiBaseUrl);
final Uri apiBaseUrl;
}
final class ApiClient {
ApiClient(this.config);
final AppConfig config;
Future<void> close() async {}
}
final class CatalogService {
CatalogService(this.client);
final ApiClient client;
}
Эти классы не импортируют Ark DI и не обращаются к глобальному Service Locator. Способ сборки графа известен только корню композиции приложения.
3. Один раз сконфигурируйте граф
final AppConfig config = AppConfig(Uri.parse('https://api.example.com'));
final DiContainer container = DiContainer.build((binder) {
binder.bindInstance<AppConfig>(config);
binder.bindLazySingleton<ApiClient>(
(resolver) => ApiClient(resolver.get<AppConfig>()),
dispose: (client) => client.close(),
);
binder.bindFactory<CatalogService>(
(resolver) => CatalogService(resolver.get<ApiClient>()),
);
});
Регистрации задают разное время жизни:
| Регистрация | Поведение |
|---|---|
bindInstance | Сохраняет переданный AppConfig; по умолчанию контейнер владеет регистрацией. |
bindLazySingleton | При первом разрешении создаёт один ApiClient, кэширует его и вызывает disposer при закрытии. |
bindFactory | При каждом разрешении создаёт новый CatalogService и не удерживает его. |
Обратный вызов конфигурации выполняется один раз. После его завершения DiBinder
становится недействительным, поэтому код приложения не может незаметно менять
регистрации.
4. Разрешите граф на границе композиции
final CatalogService first = container.get<CatalogService>();
final CatalogService second = container.get<CatalogService>();
assert(!identical(first, second));
assert(identical(first.client, second.client));
Сервисы остаются временными объектами, но оба получают один принадлежащий
контейнеру клиент. Затем приложение передаёт first нужной функции через
обычный конструктор.
5. Закройте владельца
await container.close();
При закрытии контейнер ожидает активные разрешения, закрывает дочерние scope и освобождает принадлежащие ему кэшированные объекты в порядке зависимостей. Результаты фабрик он не закрывает, потому что не удерживает их: ресурсами временного объекта владеет его потребитель.
Перед использованием дочерних scope и асинхронных регистраций прочитайте руководство по корню композиции.