依赖注入(DI)是用来创建对象及其依赖的其它对象的一种方式。 当依赖注入系统创建某个对象实例时,会负责提供该对象所依赖的对象(称为该对象的依赖)。
Angular 的依赖注入器负责创建服务的实例,并把它们注入到你想要注入的类中。
Angular 本身没法自动判断你是打算自行创建服务类的实例,还是等注入器来创建它。如果想通过注入器来创建,必须为每个服务指定服务提供商。
@Injectable 装饰器会指出这些服务或其它类是用来注入的。它还能用于为这些服务提供配置项。
这里我们使用类上的 @Injectable 装饰器来为 HeroService 配置了一个提供商。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class HeroService {
constructor() { }
}
这里providedIn 告诉 Angular,它的根注入器要负责调用 HeroService 类的构造函数来创建一个实例,并让它在整个应用中都是可用的。
providers: [
UserService
],
使用 UserService 这个注入令牌(injection token)注册了 UserService 类。
除了提供给全应用级或特定的 @NgModule 中之外,服务还可以提供给指定的组件。在组件级提供的服务只能在该组件及其子组件的注入器中使用。
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
@Component({
selector: 'app-heroes',
providers: [ HeroService ],
template: `
Heroes
`
})
export class HeroesComponent { }
服务在每个注入器的范围内是单例的。 在任何一个注入器中,最多只会有同一个服务的一个实例。
Angular DI 是一个 多级注入系统,这意味着各级注入器都可以创建它们自己的服务实例。
下面通过一个例子程序来解释多级注入器
上图是工程结构
上图解释了模块组件之间的关系
定义了provideIn是root,因此它有一个根注入器
@NgModule({
imports: [
CommonModule,
RoseRoutingModule
],
declarations: [CComponent, ShowComponent, BComponent],
providers: [MessageService]
})
export class RoseModule { }
Rose模块通过MessageService提供商定义了自己的注入器
一个应用中可能有多个注入器。 一个 Angular 应用是一个组件树。每个组件实例都有自己的注入器! 组件的树与注入器的树平行。
上图是当前的注入器树
组件A和组件App(图中未画出)共用了根注入器,而组件B和C共用了Rose模块注入器。
上面说到, 在任何一个注入器中,最多只会有同一个服务的一个实例。因此,该项目当前共两个MessageService实例,且这两个实例是独立的。
消息服务构造器中打印了一行话,用来跟踪它什么时候初始化以及初始化了几次。
启动项目后打印了一次
是因为根注入器,把它初始化了一次。
因为懒加载机制,此时还没有访问Rose模块,因此Rose模块的服务还没有被初始化。
点击A组件的add和App组件的Add按钮,可以看到,这两个组件共用同一个服务实例。
点击Rose按钮,加载Rose模块,此时Rose模块的MessageService也初始化了。
同时可以看到,Rose模块中,BC组件里里面没有任何message。
点击C组件中的Add按钮,可以看到,B组件显示的消息会和C组件保持一致,因为它们共用了同一个消息服务。
并且App组件里面显示的消息没有任何变化,说明它们所对应的消息服务是彼此独立的。
当一个组件申请获得一个依赖时,Angular 先尝试用该组件自己的注入器来满足它。 如果该组件的注入器没有找到对应的提供商,它就把这个申请转给它父组件的注入器来处理。 如果那个注入器也无法满足这个申请,它就继续转给它的父组件的注入器。 这个申请继续往上冒泡 —— 直到找到了一个能处理此申请的注入器或者超出了组件树中的祖先位置为止。 如果超出了组件树中的祖先还未找到,Angular 就会抛出一个错误。
NullInjectorError: No provider for MessageService!
下载地址