浅谈angular2中的依赖注入

引言

依赖注入,有后端背景的童鞋(尤其是熟悉java spring框架的)应该不会陌生,提到依赖注入,就不得不说一下控制反转,因为依赖注入实际上是控制反转概念的一种实现模式。

控制反转(inversion of control,IoC)原则的非正式称谓是“好莱坞法则”,它来自好莱坞的一句常用语“别打给我们,我们会打给你(don't call us, we'll call you)”,从控制权的角度来看,控制权从我被反转到了别人,我从打电话这个行为的发出者,变成了打电话这个行为的接受者。

控制“正”转可以理解为面向过程编程,定义一个main函数,在main函数里面定义所有的参与者与他们之间的交互过程,一点不能偷懒。需要用到某个对象某个类,自己去new,需要某个库函数,自己去主动调用。

控制“反”转可以理解为面向对象编程,我们有了各种适用于不同场景下的框架和设计模式,这些事物帮我们搭建好了一个整体架构,这个架构用于准备一些基本而普遍需要的基础物资。

控制反转的设计目标

  • 将执行任务这个动作和任务(具体实现)解耦
  • 让模块专注于它自己的设计目标。不需要考虑别的模块实现了什么,是如何实现的,模块之间依赖于接口
  • 让符合相同契约的模块能够互相替代

遵循控制反转的几个事物

  • 框架:有些太繁琐太基础的事情我已经帮你做好了,我做不了的那部分以坑的形式留给你了,填完坑你的工作也就完成了,后边的事情我来处理
  • 回调、调度器、事件循环:把坑填好,等到时机成熟我就来和你相会
    策略模式:坑的游戏规则我已经定义好(输入输出),你只需要按规则填坑就好
  • 模板方式模式:坑的种类和踩坑的顺序我已定义好,你只需要负责填坑
  • 工厂模式和依赖注入类似,下面详细分析

依赖注入

在任何一个有请求作用的系统当中,至少需要有两个类互相配合工作,在一个入口类下使用new关键字创建另一个类的对象实例,“我”充当一个入口类,在这个入口类中,我每次吃饭的时候都要买一双一次性筷子(每一次使用都要new一次),在这样的关系下,是“我”(即调用者)每次都要“主动”去买一次性筷子(另一个类),是我控制了筷子,在这种控制正转的关系下,放在现实生活当中,肯定是不现实的,而且人是懒惰的,他总会去创造出更加方便自己生活的想法,更确切的做法是,买一双普通的筷子(非一次性),把他放在一个“框架”当中,你需要使用的时候就对“框架”说:我想要用筷子(向框架发出请求),接着筷子就会"注入"到你的手上,而在这个过程当中,你不再是控制方,反而演变成一名请求者(虽然本身还是调用者),依赖于框架给予你资源,控制权落到了框架身上,这就是依赖注入的设计原理。

要理解依赖注入,就必须搞清楚几个问题:
参与者:都有谁
依赖:谁依赖于谁,为什么需要依赖
注入:谁注入谁,到底注入什么
控制:谁控制谁,控制什么

参与者:
一般有三方参与者,参与者1:某个对象,参与者2:DI框架,参与者3:对象所需的外部资源(类,文件等等)
依赖:
对象依赖于DI框架
为什么需要依赖:
“对象(参与者1)”需要“DI框架(参与者2)”提供所需的“外部资源(参与者3)”
谁注入谁:
DI框架注入“对象(参与者1)”
注入什么:
注入“对象(参与者1)”所需的“外部资源(参与者3)”
谁控制谁:
“DI框架(参与者2)”控制对象(参与者1)”
控制什么:
主要是控制对象所需外部资源实例的创建,管理所需重要对象的生命周期
反转:
在传统方式下,也就是正向,如果部件A需要依赖部件B,那就意味着A在内部要创建一个B的实例,也就是A依赖于B。在依赖注入机制也就是反向,如果在部件A中用到部件B,我们就应该期待B被传给A

正向

反向

依赖注入:它的作用是让框架帮你处理重要对象的生命周期的管理,不需要你显式地进行管理(对象构造和销毁)
优势:架构松耦合,测试更简单

Angular 依赖注入

在angular中,依赖注入包括三部分

  • 提供商:负责把一个令牌(可能是字符串也可能是类)映射到一个依赖的列表,它告诉angular该如何根据指定的令牌创建对象
  • 注入器:负责持有一组绑定,当外界要求创建对象时,解析这些依赖并注入它们。我们不需要创建angular注入器,angular在启动过程中会自动为我们创建一个应用级注入器(platformBrowserDynamic().bootstrapModule(AppModule)
  • 依赖:被用于注入的对象
注入器的提供商
  1. 类提供商 useClass
    providers: [Logger],这其实是注册提供商的简写表达式,完整的应该是providers: [{provide: Logger,useClass: Logger}]
    provide是令牌,用于定位依赖值和注册提供商
    useClass是提供商,用于定义对象,它用来指出注入什么以及如何注入
    某些时候,我们会请求一个不同的类来提供服务:
    providers: [{provide: Logger,useClass: BetterLogger}]
  2. 别名提供商 useExisting
    制造一个别名来引用以前注册过的令牌,比如:
    某个旧组件依赖一个OldLogger类,OldLogger和NewLogger具有相同的接口,但是由于某些原因, 我们不能升级这个旧组件并使用它。当旧组件想使用OldLogger记录消息时,我们希望改用NewLogger的单例对象来记录,不管组件请求的是新的还是旧的日志服务,依赖注入器注入的都应该是同一个单例对象。 也就是说,OldLogger应该是NewLogger的别名。如果使用useClass应用中会有两个不同的NewLogger实例,这显然不是我们想看到的,这时候就可以使用useExisting
providers: [
      NewLogger,
      {provide: OldLogger,useClass: NewLogger} //创建NewLogger的两个实例
]
      NewLogger,
      {provide: OldLogger,useExisting: NewLogger} //只创建NewLogger的一个实例
]
  1. 值提供商 useValue
    当我们需要一个常量,而它可能会根据应用的其他部分甚至环境进行重定义时,这种方式非常重要。
    官方推荐使用InjectionToken作为令牌
import { InjectionToken } from '@angular/core';
export const TITLE = new InjectionToken <string>('title');
providers:[
       {provide:TITLE, useValue: 'Hero of the Month'}
]
  1. 工厂提供商 useFactory
    使用工厂提供商进行注入,需要写一个返回任意对象的函数,工厂是创建可诸如对象的最强方式,因为我们可以在工厂函数中“为所欲为”。
providers:[{
        provide: MyComponent,
        useFactory: ()=> {
            if(loggedIn) {
               return new MyloggedComponent();
            }
            return new MyComponent();
        }
}]
依赖注入令牌

  1. 我们最常用的
    providers: [{provide: Logger,useClass: BetterLogger}]
  2. 类-接口
    使用没有被继承的抽象类作为依赖注入令牌,这种用法的类叫做:类-接口,它的好处是:提供了接口的强类型,能像正常类一样把它当做提供商令牌使用。类-接口应该只定义允许它消费者调用的成员,窄的接口有助于解耦该类的具体实现和它的消费者。
    不能使用接口作为依赖注入令牌,因为在JavaScript中并没有接口的概念,编译后angular无法识别
    更详细内容参考angular官方文档
  3. InjectionToken
    有时候依赖对象并不是一个类,它可能是一个简单的值,比如日期,数字和字符串,或者一个无形的对象,比如数组和函数。
    这样的对象没有应用程序接口,所以不能用一个类来表示,更适合表示它们的是:唯一的和符号性的令牌,一个JavaScript对象,拥有一个友好的名字,但不会与其它的同名令牌发生冲突。
    官方推荐使用InjectionToken实现(其实直接使用字符串也可以,最好还是按照官方推荐方式O(∩_∩)O)
import { InjectionToken } from '@angular/core';
export const TITLE = new InjectionToken <string>('title');
export const RUNNERS_UP= new InjectionToken <string>('RunnersUP');
providers:[
       {provide:TITLE, useValue: 'Hero of the Month'},
       {RUNNERS_UP, useFactory: runnersUpFactory(2),dep[Hero, HeroService]}
]
注册提供商

可以在NgModule或者组件中注册提供商
通常推荐在对应的模块(NgModule)中注册提供商,除非你必须把服务实例的范围限制到某个组件及其子组件树

  1. 在NgModule中注册提供商
@NgModule({
    imports:[],
    declarations:[],
    providers:[ HeroService ]
})

2.在组件中注册提供商

@Component({
    selector:'my-heros',
    template:`<h2></h2>`
    providers:[ HeroService ]
})
服务

在写一个服务时必须注意@Injectable() 是必不可少的,除非你不打算注入这个服务,因为@Injectable()相当于C++中的new()

import { Injectable } from '@angular/core';
@Injectable()
export class HeroService {
}

如何使用已注册的服务?在组件或者服务的构造函数中注入即可

constructor(
    public heroService: HeroService 
  ) {}

所有被注入的服务在angular中总是单例的,肯定有童鞋要问了,那我需要多实例的怎么办?
angular应用程序有多个依赖注入器,组成了一个与组件树平行的树状结构,所以可以在任何组件级别提供和建立服务,当组件申请一个依赖时,angular会从该组件本身的注入器开始,沿着依赖注入器的树向上查找,所以要实现多实例,在组件内注入服务即可

@host @Optional
有时候我们想限定依赖查找方式,比如只想让组件向上搜索到宿主组件,可以使用@host,当组件一层层向上查找没有找到注册的服务时,就会抛出错误,如果不想抛出错误,可以使用@Optional,它会把注入参数设置为null

import { Host } from '@angular/core';
constructor( @Host heroService: HeroService ) {}
import { Optional } from '@angular/core';
constructor( @Optional heroService: HeroService ) {}

总结

angularjs中依赖注入涉及的知识面很多,本文只是作了一个简单介绍,更详细内容参考官方文档和API

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 版本:Angular 5.0.0-alpha 依赖注入是重要的应用设计模式。它使用得非常广泛,以至于几乎每个人都称...
    soojade阅读 2,981评论 0 3
  • 什么是依赖性注入? 依赖性注入( Dependency Injection )其实不是 Angular 独有的概念...
    接灰的电子产品阅读 4,034评论 3 18
  • 一、什么是依赖注入 控制反转(IoC) 控制反转的概念最早在2004年由Martin Fowler提出,是针对面向...
    Keriy阅读 3,169评论 0 8
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 时间管理,一生都要陪伴的练习,这是第二次看此书,也是第一次写心得,自己的心情很高兴,因为自己开始有点清醒了。 叶武...
    海豚的世界阅读 1,969评论 3 6