Настройка поставщиков зависимостей¶
2.08.2022
В теме Создание и внедрение сервисов описывается использование классов в качестве зависимостей. Помимо классов, в качестве зависимостей можно использовать и другие значения, такие как булевы, строки, даты и объекты. Angular DI предоставляет необходимые API для гибкой настройки зависимостей, поэтому вы можете сделать эти значения доступными в DI.
Указание маркера провайдера¶
Если вы укажете класс сервиса в качестве маркера поставщика, инжектор по умолчанию будет инстанцировать этот класс с помощью оператора new
.
В следующем примере класс Logger
предоставляет экземпляр Logger
.
1 |
|
Однако вы можете настроить DI на использование другого класса или любого другого значения для ассоциации с классом Logger
. Таким образом, когда Logger
будет инжектирован, вместо него будет использоваться это новое значение.
Фактически, синтаксис провайдера класса — это сокращенное выражение, которое расширяется в конфигурацию провайдера, определяемую интерфейсом Provider
.
Angular расширяет значение providers
в данном случае в полный объект провайдера следующим образом:
1 |
|
Расширенная конфигурация провайдера представляет собой объект-букварь с двумя свойствами:
- Свойство
provide
содержит токен, который служит ключом для поиска значения зависимости и настройки инжектора. - Второе свойство — это объект определения провайдера, который указывает инжектору, как создать значение зависимости. Ключ определения провайдера может быть одним из следующих:
useClass
— этот параметр указывает Angular DI инстанцировать предоставленный класс при инжектировании зависимостиuseExisting
— позволяет использовать псевдоним маркера и ссылаться на любой существующий маркер.useFactory
— позволяет определить функцию, которая конструирует зависимость.useValue
— предоставляет статическое значение, которое должно быть использовано в качестве зависимости.
В следующем разделе описано, как использовать упомянутые ключи определения провайдера.
Провайдеры классов: useClass¶
Ключ провайдера useClass
позволяет вам создавать и возвращать новый экземпляр указанного класса. Вы можете использовать этот тип провайдера для замены альтернативной реализации общего класса или класса по умолчанию. Альтернативная реализация может, например, реализовать другую стратегию, расширить класс по умолчанию или эмулировать поведение реального класса в тестовом примере.
В следующем примере класс BetterLogger
будет инстанцирован, когда зависимость Logger
будет запрошена в компоненте или любом другом классе.
1 |
|
Если альтернативные провайдеры классов имеют свои собственные зависимости, укажите обоих провайдеров в свойстве метаданных провайдеров родительского модуля или компонента.
1 2 3 4 |
|
В этом примере EvenBetterLogger
отображает имя пользователя в сообщении журнала. Этот логгер получает пользователя из инжектированного экземпляра UserService
.
1 2 3 4 5 6 7 8 9 |
|
Angular DI знает, как построить зависимость UserService
, поскольку она была настроена выше и доступна в инжекторе.
Псевдоним провайдера: useExisting¶
Ключ провайдера useExisting
позволяет сопоставить один токен с другим. По сути, первый токен является псевдонимом для сервиса, связанного со вторым токеном, создавая два способа доступа к одному и тому же объекту сервиса.
В следующем примере инжектор внедряет экземпляр синглтона NewLogger
, когда компонент запрашивает либо новый, либо старый логгер. Таким образом, OldLogger
является псевдонимом для NewLogger
.
1 2 3 4 5 |
|
Убедитесь, что вы не переименовываете OldLogger
в NewLogger
с помощью useClass
, так как это создаст два разных экземпляра NewLogger
.
Провайдеры фабрик: useFactory¶
Ключ провайдера useFactory
позволяет вам создать зависимый объект путем вызова фабричной функции. При таком подходе вы можете создать динамическое значение на основе информации, доступной в DI и в других местах приложения.
В следующем примере только авторизованные пользователи должны видеть секретных героев в HeroService
. Авторизация может меняться в течение одного сеанса работы приложения, например, когда другой пользователь входит в систему.
Чтобы сохранить чувствительную к безопасности информацию в UserService
и вне HeroService
, задайте конструктору HeroService
булев флаг для управления отображением секретных героев.
1 2 3 4 5 6 7 8 9 |
|
Чтобы реализовать флаг isAuthorized
, используйте фабрику-провайдер для создания нового экземпляра логгера для HeroService
.
1 2 3 4 |
|
Функция factory имеет доступ к UserService
. Вы инжектируете Logger
и UserService
в провайдер фабрики, чтобы инжектор мог передать их функции фабрики.
1 2 3 4 5 |
|
- Поле
useFactory
указывает, что провайдер является фабричной функцией, реализация которой —heroServiceFactory
. - Свойство
deps
представляет собой массив маркеров провайдера.
Классы Logger
и UserService
служат маркерами для своих собственных классов-провайдеров. Инжектор разрешает эти маркеры и вводит соответствующие сервисы в соответствующие параметры фабричной функции heroServiceFactory
.
Захват провайдера фабрики в экспортируемой переменной heroServiceProvider
делает провайдер фабрики многократно используемым.
Провайдеры значений: useValue¶
Ключ useValue
позволяет связать фиксированное значение с маркером DI. Используйте эту технику для предоставления констант конфигурации во время выполнения, таких как базовые адреса веб-сайтов и флаги функций. Вы также можете использовать провайдер значения в модульном тестировании для предоставления имитационных данных вместо производственной службы данных. В следующем разделе представлена дополнительная информация о ключе useValue
.
Использование объекта InjectionToken
¶
Определите и используйте объект InjectionToken
для выбора маркера провайдера для неклассовых зависимостей. В следующем примере определен токен APP_CONFIG
типа InjectionToken
.
1 2 3 4 5 |
|
Необязательный параметр type, <AppConfig>
, и описание токена, app.config
, определяют назначение токена.
Далее зарегистрируйте провайдер зависимостей в компоненте, используя объект InjectionToken
из APP_CONFIG
.
1 2 3 |
|
Теперь внедрите объект конфигурации в конструктор с помощью декоратора параметров @Inject()
.
1 2 3 |
|
Интерфейсы и DI¶
Хотя интерфейс TypeScript AppConfig
поддерживает типизацию внутри класса, интерфейс AppConfig
не играет никакой роли в DI. В TypeScript интерфейс является артефактом времени проектирования и не имеет представления во время выполнения, или маркера, который может использовать система DI.
Когда транспилятор меняет TypeScript на JavaScript, интерфейс исчезает, потому что в JavaScript нет интерфейсов.
Поскольку у Angular нет интерфейса, который он мог бы найти во время выполнения, интерфейс не может быть маркером, и вы не можете его внедрить.
1 2 |
|
1 2 |
|