迈向angularjs2系列(5):依赖注入
一: 为什么要依赖注入
1.构造器引入依赖
假设一个类Car类依赖于Engine(引擎)类、Transition(变速箱)类,可使用构造器来完成。
//类似如下代码 class Engine{} class Transmission{} class Car { engine; transmisssion; constructor(){ this.engine=new Engine(); this.transmisssion=new Transmission(); }
构造函数创建似乎简洁明了,但是不灵活。Car类和其他两个类耦合度高,每当创建Car类,也会创建Engine类和Transmission类,它们的可测试性分别受到影响。
2.引入依赖注入
有后续的完善哦,这里只是简单的重构了一下。
class Engine{} class Transmission{} @Injectable //仅仅改动的地方 class Car { engine; transmisssion; constructor(engine:Engine,transmission:Transmisson){ this.engine=engine; this.transmisssion=transmission; } }
二: 配置注射器
在angular2中,使用注射器实现依赖注入机制。
完整的代码injector.ts:
import "reflect-metadata"; //引入 reflect-metadata 库来使用元数据反射 API: import { Injector,Inject,Injectable,OpaqueToken,provide,ReflectiveInjector } from "@angular/core"; const BUFFER_SIZE=new OpaqueToken("buffer-size"); //token常量,在整个应用里唯一、不可重复定义的值。 class Buffer{ //@Inject装饰器接收依赖对象的token constructor(@Inject(BUFFER_SIZE) private size:Number){ console.log(this.size); } //Buffer类接收依赖参数size } //使用生成的元数据解析依赖关系 @Injectable() class Socket { constructor(private buffer:Buffer){} } const injector: Injector = ReflectiveInjector.resolveAndCreate([ {provide: BUFFER_SIZE, useValue: '42'}, Buffer, Socket ]); //初始化注射器 injector.get(Socket);
1.导入
import "reflect-metadata";
import {
Injector,Inject,Injectable,OpaqueToken,provide,ReflectiveInjector
} from "@angular/core";
2.token的定义和Inject装饰器接收token
const BUFFER_SIZE=new OpaqueToken("buffer-size");
BUFFER_SIZE这个常量,可以看做整个应用中唯一、不可重复定义的值。
3.使用生成的元数据解析依赖关系
@Injectable()
class Socket {
constructor(private buffer:Buffer){}
}
Socket类用@Injectable()进行装饰,强制typescript编译器生成额外的元数据,用以描述指定的类能够注入哪些类型的依赖。这有点像angular1代码的压缩,一定要写成数组形式,以便压缩后的代码能够知道注入的是什么参数。
所以,如果省略掉装饰器,angular2的DI机制就不知道哪些符号对应的依赖关系需要进行注入了。
class Socket { constructor(@Inject(Buffer)private buffer:Buffer){} } //与上面代码等价哦
4.初始化注射器
const injector: Injector = ReflectiveInjector.resolveAndCreate([ {provide: BUFFER_SIZE, useValue: '42'}, Buffer, Socket ]); //使用resolveAndCreate静态方法创建Injector类的实例
resolveAndCreate是一个工厂方法,可接收一个provider数组作为参数,返回值是一个新的Injector实例。
resolvede的意思是provider将会执行一次解析过程。
5.前向引用
如果Socket类定义在Buffer类之前呢,由于Js代码的从上往下执行,会报错说Buffer没有定义undefined。
那么除了用正确的顺序来定义它们之外,还有一种解决方案,前向引用。
import {forwardRef} from"@angular/core" @Injectable() class Socket { constructor(@Inject(forwardRef(()=>Buffer)) private buffer:Buffer){} } //前向引用唯一要做的就是使用@Inject装饰器,并把forwardRef函数的执行结果传给它。
forwardRef是一个高阶函数,只接收一个函数,并负责返回需要被注入的依赖。这可以算是延迟依赖类型解析的方案。而之前的模式时第一次实例化依赖符号就会被解析,默认情况下,声明类的时候就需要解析符号。