问题描述
初学Angular
,可能对一堆注解有些懵。
我们一起通过实例来探讨Angular
的依赖注入。
一路尝试
@Injectable
一个命令建的StockService
,一个手动建的TestService
。
@Injectable({
providedIn: 'root'
})
export class StockService {
constructor(private testService: TestService) {
}
}
export class TestService {
constructor() {
}
}
TestService
未加@Injectable
注解,只是一个普通的TypeScript
类。
当前台组件中用到StockService
时,Angular
为我们构造StockService
,构造函数中依赖TestService
,然后Angular
去构造TestService
。
Uncaught (in promise): Error: StaticInjectorError(AppModule)[TestService]:
StaticInjectorError(Platform: core)[TestService]:
NullInjectorError: No provider for TestService!
然后发现控制台报错,无法提供TestService
,因为它没有被Angular
管理。
import {Injectable} from '@angular/core';
@Injectable()
export class TestService {
constructor() {
}
}
加上@Injectable
,将该类交给Augular
管理。
无效,仍然报错。发现只交给Angular
托管是不行的,我们还需要声明提供器。
提供器
需要提供器告诉Angular
如何注入相关对象。
@NgModule({
declarations: [],
imports: [],
providers: [TestService],
bootstrap: [AppComponent]
})
export class AppModule {
}
在AppModule
的providers
数组中声明TestService
提供器。
按类型提供,所以上面的声明与下面的代码是等价的。
providers: [{ provide: TestService, useClass: TestService }]
标记在AppModule
上,表示该TestService
的注入是应用在该模块上的。在该模块中,TestService
是单例的。
{ provide: TestService, useClass: TestService }
,表示当需要注入TestService
类型的对象时,使用TestService
类构造出的对象进行注入。
Angular
中,为什么@Component
、@Pipe
里能用构造函数注入@Injectable
呢?答案与Spring
一致,为什么@Controller
、@Service
里能Autowired
呢?思想相同。
作用域
@NgModule({
providers: [TestService]
})
export class AppModule {
}
@Component({
providers: [{ provide: TestService, useClass: AnotherTestService }]
})
export class StockComponent {
constructor(private testService: TestService) {
}
}
声明一个模块级的与一个组件级的提供器,表示在本模块或本组件中应该注入什么。
就像如上代码,声明在整个AppModule
模块中,注入的TestService
是TestService
类实例化的。
但是具体到组件,StockComponent
,虽然声明整个模块都使用TestService
,但是有时不符合需求,所以在此组件中使用AnotherTestService
注入。
就像Spring
中的@Primary
与@Qualifier
一样。
值提供与工厂方法
通过useValue
的值提供,可以定义一些常量,又是枚举的思想,我们使用引用,而不是用常量。
{ provide: domain, useValue: 'www.mengyunzhi.com' }
除了值提供还有工厂方法,当Angular
默认的实例化对象无法满足我们的要求时,我们要写自己的工厂函数生成我们的对象实例。
但是注意:虽然该方法叫工厂方法,但是该方法只在第一次用到该对象时执行一次,以后再需要用的还是之前构造出来的对象。
虽然叫工厂方法,但是还是单例的。
{ provide: ProductService, useFactory: () => { 返回一个对象实例 } }
摇树优化
现在如果用ng
生成的Angular
服务,标准的写法是providedIn: 'root'
。
粗略学习了一下,摇树优化就是按需加载,减小我们的包体积,缩短应用的加载时间。
@Injectable({
providedIn: 'root'
})
只要在服务本身的@Injectable()
装饰器中指定,而不是在依赖该服务的NgModule
或组件的元数据中指定,你就可以制作一个可摇树优化的提供商。
要想覆盖可摇树优化的提供商,请使用其它提供商来配置指定的NgModule
或组件的注入器,只要使用@NgModule()
或@Component()
装饰器中的providers: []
数组就可以了。
循环依赖
通过上面的学习,我们知道了Angular
中的依赖注入是通过构造函数的参数注入来实现的。
但是只要是通过构造函数实现的IOC
容器就会有问题,就像Spring
中一样,如果循环依赖了怎么解决呢?
@Injectable({
providedIn: 'root'
})
export class StockService {
constructor(private testService: TestService) {
}
}
@Injectable({
providedIn: 'root'
})
export class TestService {
constructor(private stockService: StockService) {
}
}
Circular dependency detected:
src/app/service/stock.service.ts -> src/app/service/test.service.ts -> src/app/service/stock.service.ts
Circular dependency detected:
src/app/service/test.service.ts -> src/app/service/stock.service.ts -> src/app/service/test.service.ts
与Spring
一样,如果循环了,因为是使用构造函数注入,所以一个对象都构造不出来,无法解决。开发时应避免循环依赖。
总结
万物相通。
Hello, Angular!