Angular的DI与Java略有不同,主要是在概念方面,从Java转过来容易被绕晕。记下来备忘。
要理清Angular的DI机制,首先要弄懂Angular中的两个概念:注入器和提供商(提供器)
注入器
在Angular中,每一个组件都会有一个依赖注入器负责注入组件需要的对象实例,一般情况下我们不需要直接调用注入器。Angular在初始化组件的时候会从类的构造函数中读取需要注入的对象并且注入。如:
constructor(private heroSerivce:HeroService){...}
Angular在看到这一行的时候,会去IOC容器中寻找ProductService的实例,并且将其注入到productService对象中。这一点类似于Java中的@Autowired注解所执行的操作。
既然这样就会出现一个和Java中一样的问题,如何把一个对象放入到IOC容器中,以及如果有多个实例怎么选择注入哪一个。由此引出提供商(提供器)的概念。
提供商
提供商即providers。
通常在component类头上可以见到这样的代码:
@Component({
selector: 'app-heroes',
templateUrl: './app-heros.html',
styleUrls: ['./app-heros.css'],
providers: [ HeroService ]
})
export class HeroesComponent {
constructor(private heroSerivce:HeroService){...}
}
这里的providers为一个数组,这行代码的意思是在当前的组件中注册一个服务商HeroService,使得HeroService可以在当前组件中被注入进来。这里跟Java是有区别的:Angular在这里把HeroService放到了一个局部的IOC容器中,该对象仅在当前IOC容器中可以被检测到并注入。
注册服务提供商还有另一种写法,见如下:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: HeroesComponent,
})
export class HeroService {
constructor() { }
}
@Injectable装饰器(等同于Java中的注解)是定义每一个Angular服务所必须的部分,@Injectable标记该服务需要被添加到IOC容器中交由Angular进行管理,与Java中的@Service注解作用是一样的。
@Injectable装饰器可以接收一个数据参数providedIn,该参数指定当前service需要给哪一个组件注册一个提供商,即当前service需要注入到哪一个组件中去。使用此写法则无需在@Component中再去声明providers属性,因为这二者是完全等价的。
回到刚才服务商的内容中,providers: [ HeroService ]
是以下内容的简写
providers: [{provide: HeroService, useClass: HeroService}]
第一个字段provide指定了提供商的token(没错就是那个表示令牌的token),第二个字段useClass说明我要以new的方式创建一个HeroService的实例。token要与constructor中声明的类型一致。结合上文的constructor来一起看,注入的过程是这样的:
当注入器发现我需要一个HeroService类型的对象的时候,会去IOC容器中寻找token是HeroService的对象(即provide所指定的类型),如果找到了,则用useClass属性指定的对象来实例化HeroService并将结果注入到heroService变量中。当然也会有如下可能:
providers: [{provide: HeroService, useClass: AnotherHeroService}]
这种情况下便会new一个AnotherHeroService注入到heroService中。类比Java中子类与父类的关系这里还是相对容易理解的。
除此之外还可以用工厂方法来返回一个具体的实例,如:
providers: [{provide: HeroService, useFactory: ()=>{......}}]
暂时还没有使用到,等日后理解再来补充。
如果我想要把一个服务放到全局IOC容器中该怎么做,可以这样写:
@Injectable({
providedIn: 'root',
})
root为Angular的根注入器,这样声明之后便可以在所有的组件constructor中直接声明所需要的服务。当然也可以在app.moudle.ts中的providers去声明,效果是一样的。