依赖注入

2019-07-13  本文已影响0人  oWSQo

在依赖注入中,注入器是粘合剂,它连接了调用方和被依赖方。注入器根据Provider的配置来生成依赖对象,调用方根据Provider提供的标识告诉注入器来获取被依赖的对象。

在组件中注入服务

在组件中使用依赖注入需要三步:

//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基于标识来指定依赖项,这种方式可以使得依赖项能够被动态指定为其他不同的具体实现,只要接口不变,对于使用方就是透明的。

//渲染方式
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是一个数组属性,指定了所需要的依赖,可以注入到工厂方法中。

上一篇 下一篇

猜你喜欢

热点阅读