angular学习之路19-Angular 中的依赖注入

创建和注册可注入的服务

DI 框架让你能从一个可注入的服务类(独立文件)中为组件提供数据。为了演示,我们还会创建一个用来提供英雄列表的、可注入的服务类,并把它注册为该服务的提供商。

创建可注入的服务类

Angular CLI 可以用下列命令在 src/app/heroes 目录下生成一个新的 HeroService 类。

ng generate service heroes/hero

上列命令会创建 HeroService 的骨架。

import { Injectable } from '@angular/core';

@Injectable({  //依赖注入
  providedIn: 'root',
})
export class HeroService {
  constructor() { }
}

@Injectable() 是每个 Angular 服务定义中的基本要素。该类的其余部分导出了一个 getHeroes 方法,它会返回像以前一样的模拟数据。

import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';

@Injectable({
  // we declare that this service should be created
  // by the root application injector.
  providedIn: 'root',
})
export class HeroService {
  getHeroes() { return HEROES; }
}

用服务提供商配置注入器

我们创建的类提供了一个服务。@Injectable() 装饰器把它标记为可供注入的服务,不过在你使用该服务的 provider 提供商配置好 Angular 的依赖注入器之前,Angular 实际上无法将其注入到任何位置。

该注入器负责创建服务实例,并把它们注入到像 HeroListComponent 这样的类中。 你很少需要自己创建 Angular 的注入器。Angular 会在执行应用时为你创建注入器,第一个注入器是根注入器,创建于启动过程中。

提供商会告诉注入器如何创建该服务。 要想让注入器能够创建服务(或提供其它类型的依赖),你必须使用某个提供商配置好注入器。

提供商可以是服务类本身,因此注入器可以使用 new 来创建实例。 你还可以定义多个类,以不同的方式提供同一个服务,并使用不同的提供商来配置不同的注入器。

你可以在三种位置之一设置元数据,以便在应用的不同层级使用提供商来配置注入器:

  • 在服务本身的 @Injectable() 装饰器中   //提供商是在injectable里面定义的

  • 在 NgModule 的 @NgModule() 装饰器中。//在ngmodule中定义则是根组件提供商

  • 在组件的 @Component() 装饰器中。  //组件提供商

@Injectable() 装饰器具有一个名叫 providedIn 的元数据选项,在那里你可以指定把被装饰类的提供商放到 root 注入器中,或某个特定 NgModule 的注入器中。

@NgModule() 和 @Component() 装饰器都有用一个 providers 元数据选项,在那里你可以配置 NgModule 级或组件级的注入器。

注入服务

HeroListComponent 要想从 HeroService 中获取英雄列表,就得要求注入 HeroService,而不是自己使用 new 来创建自己的 HeroService 实例。

你可以通过制定带有依赖类型的构造函数参数来要求 Angular 在组件的构造函数中注入依赖项。下面的代码是 HeroListComponent 的构造函数,它要求注入 HeroService

constructor(heroService: HeroService)  //通过构造函数来注入服务

当然,HeroListComponent 还应该使用注入的这个 HeroService 做一些事情。 这里是修改过的组件,它转而使用注入的服务。与前一版本并列显示,以便比较。

import { Component }   from '@angular/core';
import { Hero }        from './hero';
import { HeroService } from './hero.service';

@Component({
  selector: 'app-hero-list',
  template: `
    <div *ngFor="let hero of heroes">
      {{hero.id}} - {{hero.name}}
    </div>
  `
})
export class HeroListComponent {
  heroes: Hero[];

  constructor(heroService: HeroService) {  //构造函数注入服务
    this.heroes = heroService.getHeroes();  //从服务中获取数据
  }
}

 

注入器树与服务实例

在某个注入器的范围内,服务是单例的。也就是说,在指定的注入器中最多只有某个服务的最多一个实例。

应用只有一个根注入器。在 root 或 AppModule 级提供 UserService 意味着它注册到了根注入器上。 在整个应用中只有一个 UserService 实例,每个要求注入 UserService 的类都会得到这一个服务实例,除非你在子注入器中配置了另一个提供商。

Angular DI 具有分层注入体系,这意味着下级注入器也可以创建它们自己的服务实例。 Angular 会有规律的创建下级注入器。每当 Angular 创建一个在 @Component() 中指定了 providers 的组件实例时,它也会为该实例创建一个新的子注入器。 类似的,当在运行期间加载一个新的 NgModule 时,Angular 也可以为它创建一个拥有自己的提供商的注入器。

子模块和组件注入器彼此独立,并且会为所提供的服务分别创建自己的实例。当 Angular 销毁 NgModule 或组件实例时,也会销毁这些注入器以及注入器中的那些服务实例。

借助注入器继承机制,你仍然可以把全应用级的服务注入到这些组件中。 组件的注入器是其父组件注入器的子节点,它会继承所有的祖先注入器,其终点则是应用的注入器。 Angular 可以注入该继承谱系中任何一个注入器提供的服务。

比如,Angular 既可以把 HeroComponent 中提供的 HeroService 注入到 HeroListComponent,也可以注入 AppModule 中提供的 UserService

 

那些需要其它服务的服务

服务还可以具有自己的依赖。HeroService 非常简单,没有自己的依赖。不过,如果你希望通过日志服务来报告这些活动,那么就可以使用同样的构造函数注入模式,添加一个构造函数来接收一个 Logger 参数。

这是修改后的 HeroService,它注入了 Logger,我们把它和前一个版本的服务放在一起进行对比。

import { Injectable } from '@angular/core';
import { HEROES }     from './mock-heroes';
import { Logger }     from '../logger.service';

@Injectable({
  providedIn: 'root',
})
export class HeroService {

  constructor(private logger: Logger) {  }  //通过构造函数在服务中注入另外的服务

  getHeroes() {
    this.logger.log('Getting heroes ...');
    return HEROES;
  }
}

该构造函数请求注入一个 Logger 的实例,并把它保存在一个名叫 logger 的私有字段中。 当要求获取英雄列表时,getHeroes() 方法就会记录一条消息。

注意,虽然 Logger 服务没有自己的依赖项,但是它同样带有 @Injectable() 装饰器。实际上,@Injectable() 对所有服务都是必须的

当 Angular 创建一个构造函数中有参数的类时,它会查找有关这些参数的类型,和供注入使用的元数据,以便找到正确的服务。 如果 Angular 无法找到参数信息,它就会抛出一个错误。 只有当类具有某种装饰器时,Angular 才能找到参数信息。@Injectable() 装饰器是所有服务类的标准装饰器。

依赖注入令牌

当使用提供商配置注入器时,就会把提供商和一个 DI 令牌关联起来。 注入器维护一个内部令牌-提供商的映射表,当请求一个依赖项时就会引用它。令牌就是这个映射表的键。

在简单的例子中,依赖项的值是一个实例,而类的类型则充当键来查阅它。 通过把 HeroService 类型作为令牌,你可以直接从注入器中获得一个 HeroService 实例。

heroService: HeroService;

可选依赖

HeroService 需要一个记录器,但是如果找不到它会怎么样?

当组件或服务声明某个依赖项时,该类的构造函数会以参数的形式接收那个依赖项。 通过给这个参数加上 @Optional() 注解,你可以告诉 Angular,该依赖是可选的。

import { Optional } from '@angular/core';

constructor(@Optional() private logger: Logger) {  //可选的依赖,当有可选的依赖的时候要注意处理当为空的时候的情况
  if (this.logger) {
    this.logger.log(some_message);
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值