typescript nodejs 依賴注入實(shí)現(xiàn)方法代碼詳解
依賴注入通常也是我們所說(shuō)的ioc模式,今天分享的是用typescript語(yǔ)言實(shí)現(xiàn)的ioc模式,這邊用到的主要組件是 reflect-metadata 這個(gè)組件可以獲取或者設(shè)置元數(shù)據(jù)信息,它的作用是拿到原數(shù)據(jù)后進(jìn)行對(duì)象創(chuàng)建類似C#中的反射,先看第一段代碼:
import "reflect-metadata";
/**
* 對(duì)象管理器
*/
const _partialContainer = new Map<string, any>();
const PARAMTYPES = "design:paramtypes";//需要反射的原數(shù)據(jù),有很多種選擇,我們這里選擇的是拿到構(gòu)造函數(shù)的參數(shù)類型,為了后續(xù)判斷
/**
* 局部注入器,注入的是全局服務(wù),實(shí)例是全局共享
*/
export function Inject(): ClassDecorator {
return target => {
const params: Array<any> = Reflect.getMetadata(PARAMTYPES, target);
if (params)
for (const item of params) {
if (item === target) throw new Error("不能注入自己");
}
_partialContainer.set(target.name, target);//加入到對(duì)象管理器中,這個(gè)時(shí)候?qū)ο筮€沒(méi)有被創(chuàng)建
}
}
上面的代碼是創(chuàng)建一個(gè)類級(jí)別的裝飾器,表示凡是使用了這個(gè)裝飾器的類都會(huì)被依賴注入對(duì)象管理器管理,這里沒(méi)有馬上創(chuàng)建服務(wù),原因是reflect-metadata的執(zhí)行有先機(jī)是最高的,而這個(gè)依賴注入是支持手動(dòng)注入一些實(shí)例對(duì)象,所有為了防止出現(xiàn)注入?yún)?shù)為undefined所以創(chuàng)建實(shí)例的工作是放在后面的,請(qǐng)看接下來(lái)的代碼:
/**
*
* @param type 已創(chuàng)建的實(shí)例對(duì)象
*/
export function addServiceInGlobal(...types: Array<Object>) {
for (const iterator of types) {
_partialContainer.set(iterator.constructor.name, iterator);
}
}
上面的方法是手動(dòng)注入實(shí)例對(duì)象時(shí)調(diào)用的,我們需要提高這個(gè)方法的執(zhí)行優(yōu)先級(jí),具體的實(shí)例會(huì)在后面演示,接下來(lái)是最重要部分,創(chuàng)建實(shí)例部分:
export function serviceProvider<T>(service: ServiceType<T>): T {
if (_partialContainer.has(service.name) && !_partialContainer.get(service.name).name)
return _partialContainer.get(service.name);// 如果實(shí)例已經(jīng)被創(chuàng)建就直接返回
const params: Array<any> = Reflect.getMetadata(PARAMTYPES, service);// 反射拿到構(gòu)造函數(shù)的參數(shù)類型
const constrparams = params.map(item => { // 實(shí)例化參數(shù)中的依賴
if (!_partialContainer.has(item.name)) throw new Error(`${item}沒(méi)有被注入`);// 如果沒(méi)有注入就拋出異常
if (item.length)// 表示這個(gè)類型還有其它依賴
return serviceProvider(item);// 遞歸繼續(xù)獲取其他依賴
if (_partialContainer.has(item.name) && !_partialContainer.get(item.name).name)
return _partialContainer.get(item.name);// 如果實(shí)例已經(jīng)被創(chuàng)建就直接返回
const obj = new item();// 已經(jīng)沒(méi)有其他依賴了 開(kāi)始創(chuàng)建實(shí)例
_partialContainer.set(item.name, obj);// 替換對(duì)象管理器中原來(lái)沒(méi)有實(shí)例化的對(duì)象
return obj;
});
const obj = new service(...constrparams); // 這里表示對(duì)象沒(méi)有被創(chuàng)建,開(kāi)始創(chuàng)建對(duì)象
_partialContainer.set(service.name, obj);// 替換對(duì)象管理器中原來(lái)沒(méi)有實(shí)例化的對(duì)象
return obj;
}
上面代碼寫的稍微有一點(diǎn)點(diǎn)復(fù)雜,其他理解起來(lái)也不困難,大白話講就是 如果已經(jīng)實(shí)例化了直接返回實(shí)例不然就開(kāi)始對(duì)象以及創(chuàng)建出所有的依賴。接下來(lái)是例子:
import { serviceProvider, addServiceInGlobal, Inject } from './core/injectable/injector';
import "reflect-metadata";
import moment = require('moment');
@Inject()
export class ServiceA{
property?:string;
msg(){
return "ServiceA";
}
}
@Inject()
export class ServiceC {
constructor(private service: ServiceA) { }
print() {
console.log( this.service.property);
return "調(diào)用了我";
}
}
@Inject()
export class ServiceD{
print(){
console.log("我在測(cè)試注入");
}
}
@Inject()
export class GlobalService {
constructor(private service: ServiceC) { }
msg!: string;
print() {
console.log(`共享模塊${this.service.print()}`)
}
}
@Inject()
export class Init {
constructor(private service: ServiceA,
private serviceD: ServiceD,
private global: GlobalService,
private date: Date,
private strList: string[],
private serviceC: ServiceC,
) { }
start() {
console.log(this.service.msg());
this.service.property = "A模塊設(shè)置的共享數(shù)據(jù)"
console.log(moment(this.date).format("YYYY-MM-DD"))
console.log(this.strList);
this.serviceD.print();
this.serviceC.print();
this.global.print();
}
}
const obj = new Date("2017-1-1");
const str = ['呂順彬','菜鳥(niǎo)','豆豆','大鐵','CC哥','碼農(nóng)之家的一群人'];
addServiceInGlobal(obj, str); // 添加手動(dòng)創(chuàng)建的實(shí)例對(duì)象到對(duì)象管理器
const service = serviceProvider(Init); // 開(kāi)始創(chuàng)建實(shí)例
service.start()// 執(zhí)行
上面的實(shí)例中得到一下執(zhí)行結(jié)果:

總結(jié):上面我用的是默認(rèn)全局注入,沒(méi)有做singletion (單例) ,如果要做的話稍微修改下代碼就可以實(shí)現(xiàn),這里邊的難點(diǎn)可能是基于反射的設(shè)計(jì)方法,如果前端思維可能理解起來(lái)稍微困難點(diǎn),后臺(tái)的話稍微好點(diǎn)。
總結(jié)
以上所述是小編給大家介紹的typescript nodejs 依賴注入實(shí)現(xiàn)方法代碼詳解,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!
- 詳解JavaScript私有類字段和TypeScript私有修飾符
- JS裝飾者模式和TypeScript裝飾器
- JavaScript/TypeScript 實(shí)現(xiàn)并發(fā)請(qǐng)求控制的示例代碼
- RxJS在TypeScript中的簡(jiǎn)單使用詳解
- 在Vue.js中使用TypeScript的方法
- JavaScript和TypeScript中的void的具體使用
- vue + typescript + video.js實(shí)現(xiàn) 流媒體播放 視頻監(jiān)控功能
- 手把手教你使用TypeScript開(kāi)發(fā)Node.js應(yīng)用
- JavaScript?與?TypeScript之間的聯(lián)系
相關(guān)文章
Ubuntu中搭建Nodejs開(kāi)發(fā)環(huán)境過(guò)程分享
這篇文章主要介紹了Ubuntu中搭建Nodejs開(kāi)發(fā)環(huán)境過(guò)程,比較郁悶的是apt-get安裝失敗了,如果有遇到一樣問(wèn)題的朋友,可以參考一下本文2014-06-06
Nodejs爬蟲(chóng)進(jìn)階教程之異步并發(fā)控制
這篇文章主要介紹了Nodejs爬蟲(chóng)進(jìn)階教程之異步并發(fā)控制的相關(guān)資料,需要的朋友可以參考下2016-02-02
Nodejs異步回調(diào)之異常處理實(shí)例分析
這篇文章主要介紹了Nodejs異步回調(diào)之異常處理,結(jié)合實(shí)例形式分析了nodejs基于中間件進(jìn)行異步回調(diào)異常處理過(guò)程出現(xiàn)的問(wèn)題與相應(yīng)的解決方法,需要的朋友可以參考下2018-06-06
參考EventEmitter實(shí)現(xiàn)完整訂閱發(fā)布功能函數(shù)
這篇文章主要為大家介紹了參考EventEmitter實(shí)現(xiàn)完整訂閱發(fā)布功能函數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Node.js使用SQLite數(shù)據(jù)庫(kù)方法大全
Node.js是一種流行的JavaScript運(yùn)行時(shí),提供了許多有用的模塊和庫(kù)來(lái)構(gòu)建Web應(yīng)用程序,而SQLite是一種嵌入式關(guān)系型數(shù)據(jù)庫(kù),它可以運(yùn)行在各種操作系統(tǒng)上,包括Windows、Linux和Mac OS X等,在Node.js中,可以通過(guò)安裝sqlite3模塊來(lái)訪問(wèn)SQLite數(shù)據(jù)庫(kù)2023-10-10
學(xué)習(xí) NodeJS 第八天:Socket 通訊實(shí)例
本篇文章主要介紹了學(xué)習(xí) NodeJS 第八天:Socket 通訊實(shí)例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2016-12-12
node事件循環(huán)和process模塊實(shí)例分析
這篇文章主要介紹了node事件循環(huán)和process模塊,結(jié)合實(shí)例形式分析了node事件循環(huán)和process模塊具體功能、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-02-02

