一、什么是依赖注入
控制反转(IoC)
控制反转的概念最早在2004年由Martin Fowler提出,是针对面向对象设计不断复杂化而提出的一种设计原则,是利用面向对象编程法则来降低应用耦合的设计模式。
IoC强调的是对代码引用的控制权由调用方转移到了外部容器,在运行是通过某种方式注入进来,实现了控制反转,这大大降低了程序之间的耦合度。依赖注入是最常用的一种实现IoC的方式,另一种是依赖查找。
依赖注入(Dependency Injection)
当然,按照惯例我们应该举个例子, 哦对,我们主要说明的是依赖注入,依赖查找请自行查阅资料。
假设我们有一个能做汉堡的设备(HRobot),需要用肉(meat)和一些沙拉(salad)作为原料,我们可以这样实现:
export class HRobot { public meat: Meat; public salad: Salad; constructor() { this.meat = new Meat(); this.salad = new Salad(); } cook() {} }
看一下好像没有什么问题,可能你已经发现,我们的原材料都是放在机器里面的,如果我们想吃别的口味的汉堡恐怕就要去乡村基了。
为了可以吃到别的口味的汉堡,我们不得不改造一下我们的HRobot:
export class HRobot { public meat: Meat; public salad: Salad; constructor(public meat: Meat, public salad: Salad) { this.meat = meat; this.salad = salad; } cook() {} }
现在,只要要直接给它meat和salad就好了,我们的HRobot()并不需要知道给它的是什么样的meat:
let hRobot = new HRobot(new Meat(), new Salad());
比如,我们想吃鸡肉汉堡,只需要个它一块鸡肉就好:
class Chicken extends Meat { meat = 'chiken'; } let cRobot = new HRobot(new Chicken(), new Salad());
感觉还不错,我们再也不会为了吃一个鸡肉汉堡大费周章的去改造一台机器,这太不可思议了。
我可能想到了,你还是懒得弄块鸡肉给它,这时候可以使用工厂函数:
export class HRobotFactory { createHRobot() { let robot = new HRobot(this.createMeat(), this.createSalad()); } createMeat() { return new Meat(); } creatSalad() { return new Salad(); } }
现在有了工厂,就有源源不断的汉堡可以吃了,开不开心,惊不惊喜?
好吧,没有最懒,只有更懒,连工厂都懒得管理我也是无话可说,幸运的是我们有Angular提供的依赖注入框架,它可以让你伸手就有汉堡吃!
二、 Angular依赖注入
在介绍Angular依赖注入之前,先来理一下三个概念:
注入器(Injector):就想制造工厂,提供了一系列的接口,用于创建依赖对象的实例。
提供商(Provider):用于配置注入器,注入器通过它来创建被依赖对象的实例,Provider把令牌(Token)映射到工厂方法,被依赖的对象就是通过这个方法创建的。
依赖(Denpendence):指定了被依赖对象的类型,注入器会根据此类型创建对应的对象。
说了半天到底是什么样的?
用代码示例如下:
var injector = new Injector(...); var robot = injector.get(HRobot); robot.cook();
Injector()的实现如下:
import { ReflecttiveInjector } form '@angular/core'; var injector = ReflectiveInjector.resolveAndCreat([ {provide: HRobot, useClass: HRobot}, {provide: Meat, useClass: Meat}, {provide: Salad, useClass: Salad} ]);
还有注入器是这样知道知道初始化HRobot需要依赖Meat和Salad:
export class Robot { //... consructor(public meat: Meat, public salad: Salad) {} //... }
当然,看了头大是应该的,因为上面的东西压根就不需要自己动手写,Angular的依赖注入框架已经自动帮我们完成了(注入器的生成和调用)。
1. 在组件中注入服务
Angular在底层做了大量的初始化工作,这极大地降低了我们使用依赖注入的成本,现在要完成依赖注入,我们只需要三步:
通过import导入被依赖的对象服务
在组件中配置注入器。在启动组件时,Angular会读取@Component装饰器里的providers元数据,它是一个数组,配置了该组件需要使用的所有依赖,Angular的依赖注入框架会根据这个列表去创建对应的示例。
在组件构造函数中声明需要注入的依赖。注入器会根据构造函数上的声明,在组件初始化时通过第二步中的providers元数据配置依赖,为构造函数提供对应的依赖服务,最终完成依赖注入。
例子来了:
// app.component.ts //... // 1. 导入被依赖对象的服务 import { MyService } from './my-service/my-service.service'; @Component({ //... // 2. 在组件中配置注入器 providers: [ MyService ] //... }) export class AppComponent { // 3. 在构造函数中声明需要注入的依赖 constructor(private myService: MyService) {} }
2. 在服务中注入服务