IOC的一個(gè)很重要的實(shí)現(xiàn)就是依賴注入点把,將用戶對(duì)類實(shí)例化的操作權(quán)粉捻,反轉(zhuǎn)給容器去做弧轧,由容器來(lái)處理類與類之間的依賴關(guān)系雪侥,從而可以降低模塊與模塊之間的耦合關(guān)系。
下面是手動(dòng)實(shí)現(xiàn)的依賴注入原理:
// es7的提案,ts1.5+版本已經(jīng)支持精绎,可以通過(guò)他給類或者類的原型屬性上添加元數(shù)據(jù)
import 'reflect-metadata';
const INJECTED = '__inject__';
type Constructor<T = any> = new (...args: any[]) => T;
// 定義一個(gè)裝飾器速缨,他可以在類的構(gòu)造函數(shù)上定義元數(shù)據(jù),定義的元數(shù)據(jù)是構(gòu)造函數(shù)的所有參數(shù)
const Injectable = (): ClassDecorator => (constructor) => {
Reflect.defineMetadata(
INJECTED,
Reflect.getMetadata('design:paramtypes', constructor), // 這里還支持兩外兩種內(nèi)置元數(shù)據(jù)定義代乃,一個(gè)是design:type獲取屬性類型旬牲,一個(gè)是design:returntype獲取返回值類型
constructor,
);
};
@Injectable()
class AService {
a = 1;
}
@Injectable()
class BService {
b = 2;
constructor(private aService: AService) {}
}
@Injectable()
class TestService {
constructor(
private obj,
private aService: AService,
private bService: BService,
) {}
}
const getInstance = <T>(target: Constructor<T>): T => {
// 獲取所有注入的服務(wù)
const providers = Reflect.getMetadata(INJECTED, target);
const args =
providers?.map((provider: Constructor) => {
console.log(provider);
return getInstance(provider); // 遞歸實(shí)例化所有依賴
}) ?? [];
return new target(...args);
};
console.log(getInstance(TestService));
// 打印結(jié)果:
/*
* [Function: Object]
* [class AService]
* [class BService]
* [class AService]
* TestService {
* obj: {},
* aService: AService { a: 1 },
* bService: BService { aService: AService { a: 1 }, b: 2 }
* }
*/
特點(diǎn):通過(guò)容器完成對(duì)象的裝配,注入到需要的對(duì)象(被動(dòng)獲雀橄拧)
因此依賴注入又被稱之為好萊塢原則:”Don't call us, we'll call you”原茅,有事兒不要找我們,如果有需要我們會(huì)找你堕仔!