Angular2 依赖注入之装饰器
2017-04-20 21:31
357 查看
下面会从 Angular 的源码分析依赖注入中的的装饰器,基于 angular@4.1.0-beta.1
Angular 依赖注入过程主要有下面几个重要的部分组成:
Inject 和 Injectable 装饰器
解析提供商,构造注入器
获取实例化对象
Angular 依赖注入中的一些重要的概念:
Provider :
提供商,下面就是一个Proviver,一共有5种构造提供商的方式:TypeProvider, ValueProvider, ClassProvider, ExistingProvider, FactoryProvider
Token :
令牌,提供商中的第一个参数就是Token,在查找依赖时作为 key 使用。
Injector :
注入器,用于解析依赖和创建对象。
Example
Inject装饰器会将 构造器 => [依赖0, 依赖1 …] 这种 Map 数据结构保存在全局变量中,而不是装饰的对象上。而且只是保存了这种数据关系,并不会实际创建对象。,Injector 会从前面保存的数
简单地说就是,Inject 装饰器在 window[‘__core-js_shared__’] 这个对象中保存了一个 Map = { Car() => [ Engine() ] }, 在后面在解析 Provider 的时候,Injector 就是从这里寻找对象的依赖。
当然实际还要复杂的多。
下面将一步步介绍 Inject 注入器做了哪些事,看源码:
在上面的代码编译成 JS 文件时,可以看到如下代码,__param() 函数传入 paramIndex 和 decorator 两个参数,其中 decorator 由 metadata_1.Inject() 得到,这个函数先放到后面。 __decorate 需要四个参数,因为装饰器可以用在函数或属性上面,因此需要对不同的目标作不同的处理。
在本例中只传入了2个参数,分别是 decorators 和 target ( Car ), 因此 r = target. 后面判断是否存在Reflect这个对象,因为这个对象是 es7 中的提案,一般浏览器中不存在,需要使用 polyfills 的方式引入。如果不存在,则对 decorators 中的每一个值执行 d(r),那么 d(r) 这个方法到底做了什么呢?
decorators 中的每一个 decorator 都是 __param() 的处理结果,而它的返回值是一个匿名函数,因此 d(r) 实际执行了这个匿名函数,匿名函数里面又执行了 decorator(). decorator是什么函数呢?
先看 metadata_1.Inject() 这个函数,在 metadata.ts 中 Inject 返回了 makeParamDecorator() 的结果
在 makeParamDecorator() 这个函数中定义了一个 ParamDecoratorFactory 工厂方法,并将 ParamDecoratorFactory 方法返回。 因此 metadata_1.Inject(Engine) = ParamDecoratorFactory(Engine), 并返回了一个 ParamDecorator() 函数。 所以上面的 decorator(target, key, paramIndex) = ParamDecorator (Car, undefined, 0);
接下来看 ParamDecorator(), 首先通过 Reflect.Reflect() 获取参数parameters,然后将 ParamDecoratorFactory 的一个实例(内部保存了 Engine 信息)放到参数数组里面。
Reflect.getOwnMetadata() 这个函数会获取对象上所保存的元数据信息,本质是一个 Map 对象。 如果没有找到便创建一个空的 Map. 其中 Reflect 静态方法所保存的信息在全局对象 window[‘__core-js_shared__’] 中,并不是存在注解所对应的对象中。直接在控制台输入可以看到相应的信息。
下面是一个实际的数据结构,具体为什么要搞这么复杂的结构我也是懵逼的…
可能还要看看 Reflect 中的实现了…
这样就可以将一个对象 (这里是 Car ) 和多个 ParamDecoratorFactory 实例 (这里是Engine依赖) 关联在一起。而 ParamDecoratorFactory 实例中包含了依赖的信息。后面在寻找依赖,创建对象就是从 Reflect 中获取信息的。
构造 ParamDecoratorFactory 时使用了 makeMetadataCtor() 函数,这个函数传入参数 [[‘token’, undefined]] 就是将 token : Engine 这条属性赋值给 ParamDecoratorFactory 实例。
Angular 官方文档里面说了如果一个服务没有依赖其他的服务,即构造器中的参数为空,Injectable 注解是可有可无的,但是如果有依赖,必须加上 @Injectable()。
实际上使用了@Injectable()装饰的类,其构造函数中的参数就可以省略@Inject()。
使用 @Injectable 注解的类会 TypeScript 会编译成以下代码。和上面不同的是,这里把依赖信息放在 “design:paramtypes” 中,而前面则是放在 “parameters” 里面。
在实际查找依赖信息时,有下面的代码。 依赖解析时分别获取 ‘parameters’ 和 ‘design:paramtypes’ 这两个的元数据。所以 @Injectable() 装饰器也能保存依赖信息。
Angular 依赖注入过程主要有下面几个重要的部分组成:
Inject 和 Injectable 装饰器
解析提供商,构造注入器
获取实例化对象
Angular 依赖注入中的一些重要的概念:
Provider :
提供商,下面就是一个Proviver,一共有5种构造提供商的方式:TypeProvider, ValueProvider, ClassProvider, ExistingProvider, FactoryProvider
{ provide: Logger, useClass: Logger }
Token :
令牌,提供商中的第一个参数就是Token,在查找依赖时作为 key 使用。
Injector :
注入器,用于解析依赖和创建对象。
Example
class Engine { start() { console.log('engine start'); } } class Car { engine: Engine; constructor(@Inject(Engine) engine) { this.engine = engine; } open() { this.engine.start(); console.log('car open'); } } let inj = ReflectiveInjector.resolveAndCreate([ Car, Engine ]); let car = inj.get(Car); car.open();
1. 装饰器
下面主要讲 Inject 和 Injectable 两个装饰器。1.1 Inject 装饰器
构造器中的@Inject()指明了需要注入的对象类型,这里使用TypeScript的装饰器模式。Inject装饰器会将 构造器 => [依赖0, 依赖1 …] 这种 Map 数据结构保存在全局变量中,而不是装饰的对象上。而且只是保存了这种数据关系,并不会实际创建对象。,Injector 会从前面保存的数
简单地说就是,Inject 装饰器在 window[‘__core-js_shared__’] 这个对象中保存了一个 Map = { Car() => [ Engine() ] }, 在后面在解析 Provider 的时候,Injector 就是从这里寻找对象的依赖。
当然实际还要复杂的多。
下面将一步步介绍 Inject 注入器做了哪些事,看源码:
在上面的代码编译成 JS 文件时,可以看到如下代码,__param() 函数传入 paramIndex 和 decorator 两个参数,其中 decorator 由 metadata_1.Inject() 得到,这个函数先放到后面。 __decorate 需要四个参数,因为装饰器可以用在函数或属性上面,因此需要对不同的目标作不同的处理。
在本例中只传入了2个参数,分别是 decorators 和 target ( Car ), 因此 r = target. 后面判断是否存在Reflect这个对象,因为这个对象是 es7 中的提案,一般浏览器中不存在,需要使用 polyfills 的方式引入。如果不存在,则对 decorators 中的每一个值执行 d(r),那么 d(r) 这个方法到底做了什么呢?
decorators 中的每一个 decorator 都是 __param() 的处理结果,而它的返回值是一个匿名函数,因此 d(r) 实际执行了这个匿名函数,匿名函数里面又执行了 decorator(). decorator是什么函数呢?
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var metadata_1 = require("../packages/core/src/di/metadata"); //省略中间代码 //... Car = __decorate([ __param(0, metadata_1.Inject(Engine)) ], Car);
先看 metadata_1.Inject() 这个函数,在 metadata.ts 中 Inject 返回了 makeParamDecorator() 的结果
export const Inject: InjectDecorator = makeParamDecorator('Inject', [['token', undefined]]);
在 makeParamDecorator() 这个函数中定义了一个 ParamDecoratorFactory 工厂方法,并将 ParamDecoratorFactory 方法返回。 因此 metadata_1.Inject(Engine) = ParamDecoratorFactory(Engine), 并返回了一个 ParamDecorator() 函数。 所以上面的 decorator(target, key, paramIndex) = ParamDecorator (Car, undefined, 0);
export function makeParamDecorator( name: string, props: ([string, any] | {[name: string]: any})[], parentClass?: any): any { const metaCtor = makeMetadataCtor(props); function ParamDecoratorFactory(...args: any[]): any { if (this instanceof ParamDecoratorFactory) { metaCtor.apply(this, args); return this; } const annotationInstance = new (<any>ParamDecoratorFactory)(...args); (<any>ParamDecorator).annotation = annotationInstance; return ParamDecorator; function ParamDecorator(cls: any, unusedKey: any, index: number): any { const parameters: (any[] | null)[] = Reflect.getOwnMetadata('parameters', cls) || []; // there might be gaps if some in between parameters do not have annotations. // we pad with nulls. while (parameters.length <= index) { parameters.push(null); } parameters[index] = parameters[index] || []; parameters[index] !.push(annotationInstance); Reflect.defineMetadata('parameters', parameters, cls); return cls; } } if (parentClass) { ParamDecoratorFactory.prototype = Object.create(parentClass.prototype); } ParamDecoratorFactory.prototype.toString = () => `@${name}`; (<any>ParamDecoratorFactory).annotationCls = ParamDecoratorFactory; return ParamDecoratorFactory; }
接下来看 ParamDecorator(), 首先通过 Reflect.Reflect() 获取参数parameters,然后将 ParamDecoratorFactory 的一个实例(内部保存了 Engine 信息)放到参数数组里面。
Reflect.getOwnMetadata() 这个函数会获取对象上所保存的元数据信息,本质是一个 Map 对象。 如果没有找到便创建一个空的 Map. 其中 Reflect 静态方法所保存的信息在全局对象 window[‘__core-js_shared__’] 中,并不是存在注解所对应的对象中。直接在控制台输入可以看到相应的信息。
window['__core-js_shared__']
下面是一个实际的数据结构,具体为什么要搞这么复杂的结构我也是懵逼的…
可能还要看看 Reflect 中的实现了…
这样就可以将一个对象 (这里是 Car ) 和多个 ParamDecoratorFactory 实例 (这里是Engine依赖) 关联在一起。而 ParamDecoratorFactory 实例中包含了依赖的信息。后面在寻找依赖,创建对象就是从 Reflect 中获取信息的。
构造 ParamDecoratorFactory 时使用了 makeMetadataCtor() 函数,这个函数传入参数 [[‘token’, undefined]] 就是将 token : Engine 这条属性赋值给 ParamDecoratorFactory 实例。
function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any { return function ctor(...args: any[]) { props.forEach((prop, i) => { const argVal = args[i]; if (Array.isArray(prop)) { // plain parameter this[prop[0]] = argVal === undefined ? prop[1] : argVal; } else { for (const propName in prop) { this[propName] = argVal && argVal.hasOwnProperty(propName) ? argVal[propName] : prop[propName]; } } }); }; }
1.2 Injectable 装饰器
Inject 是用来修饰属性的,而 Injectable 则是用于修饰构造函数的。我们一般在定义一个服务的时候会用以下的方式。注意这里的构造函数,没有使用 @Inject(Engine),但是使用了 @injectable 装饰类。@Injectable() class Car { engine: Engine; constructor(engine:Engine) { this.engine = engine; } open() { this.engine.start(); console.log('car open'); } }
Angular 官方文档里面说了如果一个服务没有依赖其他的服务,即构造器中的参数为空,Injectable 注解是可有可无的,但是如果有依赖,必须加上 @Injectable()。
实际上使用了@Injectable()装饰的类,其构造函数中的参数就可以省略@Inject()。
使用 @Injectable 注解的类会 TypeScript 会编译成以下代码。和上面不同的是,这里把依赖信息放在 “design:paramtypes” 中,而前面则是放在 “parameters” 里面。
var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Car = __decorate([ metadata_1.Injectable(), __metadata("design:paramtypes", [Engine]) ], Car);
在实际查找依赖信息时,有下面的代码。 依赖解析时分别获取 ‘parameters’ 和 ‘design:paramtypes’ 这两个的元数据。所以 @Injectable() 装饰器也能保存依赖信息。
// API for metadata created by invoking the decorators. if (this._reflect != null && this._reflect.getOwnMetadata != null) { const paramAnnotations = this._reflect.getOwnMetadata('parameters', type); const paramTypes = this._reflect.getOwnMetadata('design:paramtypes', type); if (paramTypes || paramAnnotations) { return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } }
相关文章推荐
- (七)理解angular中的module和injector,即依赖注入
- Angular 4依赖注入学习教程之Injectable装饰器(六)
- angular依赖注入和路由
- (七)理解angular中的module和injector,即依赖注入
- Angular.JS学习之依赖注入$injector详析
- Angular 4依赖注入学习教程之ValueProvider的使用(七)
- Angular 依赖注入
- 在Angular.js使用组合+依赖注入而不是继承
- angular 依赖注入factory
- Angular 4依赖注入学习教程之InjectToken的使用(八)
- -_-#【Angular】依赖注入
- angular2.0下的依赖注入
- Angular 2中的依赖注入
- Angular2 之 依赖注入
- Angular 4 依赖注入
- 用原生js简单模仿angular的依赖注入
- Angular 理解module和injector,即依赖注入
- AngularDart4.0 指南- 依赖注入
- 议 Angular 依赖注入
- Angular学习笔记(六)之依赖注入入门