Angular 依賴注入

問題描述

初學Angular,可能對一堆註解有些懵。typescript

咱們一塊兒經過實例來探討Angular的依賴注入。bootstrap

一路嘗試

@Injectable

一個命令建的StockService,一個手動建的TestService數組

@Injectable({
    providedIn: 'root'
})
export class StockService {

    constructor(private testService: TestService) {
    }
}
export class TestService {

    constructor() {
    }
}

TestService未加@Injectable註解,只是一個普通的TypeScript類。promise

當前臺組件中用到StockService時,Angular爲咱們構造StockService,構造函數中依賴TestService,而後Angular去構造TestServiceapp

clipboard.png

Uncaught (in promise): Error: StaticInjectorError(AppModule)[TestService]: 
StaticInjectorError(Platform: core)[TestService]: 
NullInjectorError: No provider for TestService!

而後發現控制檯報錯,沒法提供TestService,由於它沒有被Angular管理。dom

import {Injectable} from '@angular/core';

@Injectable()
export class TestService {

    constructor() {
    }
}

加上@Injectable,將該類交給Augular管理。ide

clipboard.png

無效,仍然報錯。發現只交給Angular託管是不行的,咱們還須要聲明提供器。函數

提供器

須要提供器告訴Angular如何注入相關對象。學習

@NgModule({
    declarations: [],
    imports: [],
    providers: [TestService],
    bootstrap: [AppComponent]
})
export class AppModule {
}

clipboard.png

AppModuleproviders數組中聲明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模塊中,注入的TestServiceTestService類實例化的。

可是具體到組件,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中同樣,若是循環依賴了怎麼解決呢?

clipboard.png

@Injectable({
    providedIn: 'root'
})
export class StockService {

    constructor(private testService: TestService) {
    }
}
@Injectable({
    providedIn: 'root'
})
export class TestService {

    constructor(private stockService: StockService) {
    }
}

clipboard.png

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!

相關文章
相關標籤/搜索