在依赖注入中,注入器是粘合剂,它连接了调用方和被依赖方。注入器根据Provider的配置来生成依赖对象,调用方根据Provider提供的标识告诉注入器来获取被依赖的对象。
- 注入器(Injector):提供一系列的接口用于创建依赖对象的实例。
- Provider:用于配置注入器,注入器通过它来创建被依赖对象的实例,Provider把标识(Token)映射到工厂方法,被依赖的对象就是通过该方法来创建的。
- 依赖(Dependence):指定了被依赖对象的类型,注入器会根据此类型创建对应的对象。
在组件中注入服务
在组件中使用依赖注入需要三步:
- 通过
import
导入被依赖对象的服务。 - 在组件中配置注入器。在启动组件时,angular会读取
@Component
装饰器里的providers
元数据,它是一个数组,配置了该组件需要使用到的所有依赖,angular的依赖注入框架会根据这个列表去创建对应对象的实例。 - 在组件构造函数中声明需要注入的依赖。注入器会根据构造函数上的声明,在组件初始化时通过第二步中的
providers
元数据配置依赖,为构造函数提供对应的依赖服务,最终完成注入的过程。
//app.component.ts
import { Component } from '@angular/core';
//1.导入被依赖对象的服务
import { ContactService } from './shared/contact.service';
import { LoggerService } from './shared/logger.service';
import { UserService } from './shared/user.service';
@Component({
moduleId:module.id,
selector:'contact-app',
//2.在组件中配置注入器
providers[ContactService,LoggerService,UserService],
templateUrl:'./app.component.html',
styleUrls:['./app.component.css']
})
export class ContactAppComponent {
//3.在组件构造函数中声明需要注入的依赖
constructor(
logger:LoggerService,
contactService:ContactService,
userService:UserService
) {}
}
在ContactAppComponent这个根组件中配置了providers元数据,这使得ContactAppComponent及其所有子组件,都能共享由根组件注入器创建的实例。CollectionComponent是ContactAppComponent的子组件,它并没有在@Component中添加providers元数据来注入ContactService服务,但依然可以在构造函数中获取到ContactService服务的实例。
//collection.component.ts
import { Component,OnInit } from '@angular/core';
import { ContactService } from './shanred/contact.service';
@Component({
selector:'call-record',
templateUrl:'./app/collection/collection.component.html',
styleUrls:['./app/collection/collection.component.css']
})
export class CollectionComponent implements OnInit{
collections:any=[];
contacts:any={};
constructor(private _contactService:ContactService) {}
}
在服务中注入服务
除了组件依赖服务,服务间的相互调用也很常见。
//contact.service.ts
import { Injectable } from '@angular/core';
import { LoggerService } from ./logger.service';
import { UserService } from './user.service';
@Injectable() //1.添加装饰器`@Injectable()`
export class ContactService {
//2.构造函数中注入所依赖的服务
constructor(_logger:LoggerService,_userService:UserService){}
getCollections() {
this._logger.log('Getting contacts...');
}
}
接下来,需要在组件中注册这个服务,这个服务可能会被多个模块调用,可以在根模块的providers元数据中注册它。
//...
//3.在组件的providers元数据中注册服务
providers:[ContactService,LoggerService,UserService]
//...
@Injectable不是必须的,只有当一个服务依赖其他服务的时候,才需要用@Injectable来显式装饰。推荐任何时候都加上@Injectable装饰器。
在模块中注入服务
在模块中注入的服务,属于这个模块的所有组件都能共享这个服务。
angular在启动程序时会启动一个根模块,并加载它所依赖的其他模块,此时会生成一个全局的根注入器,由该注入器创建的依赖注入对象在整个应用程序级别可见,并共享一个实例。同时,根模块会指定一个根组件并启动,由该根组件添加的依赖注入对象是组件树级别可见,在根组件以及子组件中共享一个实例。
//app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { LoggerService } from './shared/logger.service';
import { UserService } from './shared/user.service';
//假设有个单独的通讯录模块
import { ContactModule } from './contact.module';
@NgModule({
imports:[
BrowserModule,
ContactModule
],
declarations:[AppComponent],
providers:[LoggerService,UserService],
bootstrap:[AppComponent]
})
export class AppModule {}
Provider
Provider实现逻辑操作或数据操作的封装,以接口的方式提供给调用方使用。
在angular中,Provider描述了注入器如何初始化标识所对应的依赖服务,它最终用于注入到组件或者其他服务中。Provider提供了一个运行时所需的依赖,注入器依靠它来创建服务对象的实例。
@Component({
providers:[{provide:LoggerService,useClass:LoggerService}]
})
上面代码的完整形式采用了对象字面量的方式来描述一个Provider的构成要素。其中provide属性可以理解为这个Provider的唯一标识,用于定位依赖值以及注册Provider,也就是应用中使用的服务名,而useClass属性则代表使用哪个服务类去创建实例。
Provider注册方式
Provider的主要作用是注册并返回合适的服务对象,angular提供了常见的四种Provider注册形式:
- 类Provider
- 值Provider
- 别名Provider
- 工厂Provider
类Provider
类Provider基于标识来指定依赖项,这种方式可以使得依赖项能够被动态指定为其他不同的具体实现,只要接口不变,对于使用方就是透明的。
//渲染方式
var injector=Injector.resolveAndCreate([
{provider:Render,useClass:DomRender} //DOM渲染方式
//{provider:Render,useClass:CanvasRender} //Canvas渲染方式
//{provider:Render,useClass:ServerRender} //服务端渲染方式
]);
//调用方
class ApplicationComponent{
constructor(_render:Render){
_render.render();//渲染
}
}
值Provider
依赖的对象不一定是类,也可以是常量、字符串、对象等其他数据类型的,这可以方便用在全局变量、系统相关参数配置等场景中。在创建Provider对象时,只需使用useValue就可声明一个值Provider。
let globalSetting={
env:'production',
getHost:()=>{ return 'https://angular.io' }
};
@Component({
selector:'some-component',
template:'<div>Some Component</div>',
providers:[
{provide:'urlSetting',useValue:globalSetting},//对象
{provide:'NAME',useValue:'angular2'}//常量
]
})
export class SomeComponent{
constructor() {}
}
别名Provider
有了别名Provider,就可以在一个Provider中配置多个标识,其对应的对象指向同一个实例,从而实现多个依赖、一个对象实例的作用。useExisting可以用来指定一个别名Provider。
//...
providers:[
{provide:NewLoggerService,useClass:NewLoggerService},
{provide:OldLoggerService,useExisting:NewLoggerService}
]
工厂Provider
有时候依赖对象是不明确且动态变化的,可能需要根据运行环境、执行权限来生成,Provider需要一种动态生成依赖对象的能力。angular提供的工厂Provider可以解决这个问题,它通过暴露一个工厂方法,返回最终依赖的对象。
let contactServiceFactory=(_logger:LoggerService,_userService:UserService)=>
{
return new contactService(_logger,_userService.user.isAuthorized);
}
export let contactServiceProvider={
{ provide:ContactService,useFactory:contactServiceFactory,deps:[
LoggerService,UserService
]}
}
使用工厂Provider的注册方式需要用useFactory来声明Provider是一个工厂方法deps是一个数组属性,指定了所需要的依赖,可以注入到工厂方法中。
网友评论