typescript nodejs 依赖注入实现方法代码详解


Posted in NodeJs onJuly 21, 2019

依赖注入通常也是我们所说的ioc模式,今天分享的是用typescript语言实现的ioc模式,这边用到的主要组件是 reflect-metadata 这个组件可以获取或者设置元数据信息,它的作用是拿到原数据后进行对象创建类似C#中的反射,先看第一段代码:

import "reflect-metadata";
/**
 * 对象管理器
 */
const _partialContainer = new Map<string, any>();
const PARAMTYPES = "design:paramtypes";//需要反射的原数据,有很多种选择,我们这里选择的是拿到构造函数的参数类型,为了后续判断
/**
 * 局部注入器,注入的是全局服务,实例是全局共享
 */
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);//加入到对象管理器中,这个时候对象还没有被创建
  }
}

   上面的代码是创建一个类级别的装饰器,表示凡是使用了这个装饰器的类都会被依赖注入对象管理器管理,这里没有马上创建服务,原因是reflect-metadata的执行有先机是最高的,而这个依赖注入是支持手动注入一些实例对象,所有为了防止出现注入参数为undefined所以创建实例的工作是放在后面的,请看接下来的代码:

/**
 *
 * @param type 已创建的实例对象
 */
export function addServiceInGlobal(...types: Array<Object>) {
  for (const iterator of types) {
    _partialContainer.set(iterator.constructor.name, iterator);
  }
}

 上面的方法是手动注入实例对象时调用的,我们需要提高这个方法的执行优先级,具体的实例会在后面演示,接下来是最重要部分,创建实例部分:

export function serviceProvider<T>(service: ServiceType<T>): T {
  if (_partialContainer.has(service.name) && !_partialContainer.get(service.name).name)
    return _partialContainer.get(service.name);// 如果实例已经被创建就直接返回
 
  const params: Array<any> = Reflect.getMetadata(PARAMTYPES, service);// 反射拿到构造函数的参数类型
  const constrparams = params.map(item => { // 实例化参数中的依赖
    if (!_partialContainer.has(item.name)) throw new Error(`${item}没有被注入`);// 如果没有注入就抛出异常
    if (item.length)// 表示这个类型还有其它依赖
      return serviceProvider(item);// 递归继续获取其他依赖
    if (_partialContainer.has(item.name) && !_partialContainer.get(item.name).name)
      return _partialContainer.get(item.name);// 如果实例已经被创建就直接返回
    const obj = new item();// 已经没有其他依赖了 开始创建实例
    _partialContainer.set(item.name, obj);// 替换对象管理器中原来没有实例化的对象
    return obj;
  });
  const obj = new service(...constrparams); // 这里表示对象没有被创建,开始创建对象
  _partialContainer.set(service.name, obj);// 替换对象管理器中原来没有实例化的对象
  return obj;
}

 上面代码写的稍微有一点点复杂,其他理解起来也不困难,大白话讲就是 如果已经实例化了直接返回实例不然就开始对象以及创建出所有的依赖。接下来是例子:

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 "调用了我";
  }
}
@Inject()
export class ServiceD{
  print(){
   console.log("我在测试注入");
  }
}
@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模块设置的共享数据"
    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 = ['吕顺彬','菜鸟','豆豆','大铁','CC哥','码农之家的一群人'];
addServiceInGlobal(obj, str); // 添加手动创建的实例对象到对象管理器
const service = serviceProvider(Init); // 开始创建实例
service.start()// 执行

上面的实例中得到一下执行结果:

typescript nodejs 依赖注入实现方法代码详解

总结:上面我用的是默认全局注入,没有做singletion (单例) ,如果要做的话稍微修改下代码就可以实现,这里边的难点可能是基于反射的设计方法,如果前端思维可能理解起来稍微困难点,后台的话稍微好点。

总结

以上所述是小编给大家介绍的typescript nodejs 依赖注入实现方法代码详解,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

NodeJs 相关文章推荐
利用NodeJS的子进程(child_process)调用系统命令的方法分享
Jun 05 NodeJs
使用upstart把nodejs应用封装为系统服务实例
Jun 01 NodeJs
nodejs npm install全局安装和本地安装的区别
Jun 05 NodeJs
nodejs 整合kindEditor实现图片上传
Feb 03 NodeJs
nodejs爬虫抓取数据之编码问题
Jul 03 NodeJs
nodejs的HTML分析利器node-jquery用法浅析
Nov 08 NodeJs
nodejs 实现钉钉ISV接入的加密解密方法
Jan 16 NodeJs
Nodejs 获取时间加手机标识的32位标识实现代码
Mar 07 NodeJs
详解NODEJS的http实现
Jan 04 NodeJs
详解Nodejs mongoose
Jun 10 NodeJs
基于Nodejs的Tcp封包和解包的理解
Sep 19 NodeJs
Nodejs实现WebSocket代码实例
May 19 NodeJs
nodejs 递归拷贝、读取目录下所有文件和目录
Jul 18 #NodeJs
nodejs二进制与Buffer的介绍与使用
Jul 11 #NodeJs
nodejs中各种加密算法的实现详解
Jul 11 #NodeJs
监控Nodejs的性能实例代码
Jul 02 #NodeJs
搭建一个nodejs脚手架的方法步骤
Jun 28 #NodeJs
独立部署小程序基于nodejs的服务器过程详解
Jun 24 #NodeJs
nodejs实现获取本地文件夹下图片信息功能示例
Jun 22 #NodeJs
You might like
PHP生成不同颜色、不同大小的tag标签函数
2013/09/23 PHP
PHP正则提取不包含指定网址的图片地址的例子
2014/04/21 PHP
PHP+FFMPEG实现将视频自动转码成H264标准Mp4文件
2014/09/24 PHP
php的ZipArchive类用法实例
2014/10/20 PHP
PHP+MySQL使用mysql_num_rows实现模糊查询图书信息功能
2018/05/31 PHP
jQuery(1.3.2) 7行代码搞定跟随屏幕滚动的层
2009/05/21 Javascript
Javascript笔记一 js以及json基础使用说明
2010/05/22 Javascript
nodeType属性返回被选节点的节点类型介绍
2013/11/22 Javascript
JavaScript/Js脚本处理html元素的自定义属性解析(亲测兼容Firefox与IE)
2013/11/25 Javascript
使用JS+plupload直接批量上传图片到又拍云
2014/12/01 Javascript
javascript实现淡蓝色的鼠标拖动选择框实例
2015/05/09 Javascript
基于 Node.js 实现前后端分离
2016/04/23 Javascript
jQuery UI结合Ajax创建可定制的Web界面
2016/06/22 Javascript
基于jQuery实现选项卡效果
2017/01/04 Javascript
基于vue2.x的电商图片放大镜插件的使用
2018/01/22 Javascript
vue spa应用中的路由缓存问题与解决方案
2019/05/31 Javascript
layui弹出框Tab选项卡的示例代码
2019/09/04 Javascript
vue.js路由mode配置之去掉url上默认的#方法
2019/11/01 Javascript
js 计算月/周的第一天和最后一天代码
2020/02/01 Javascript
linux系统使用python监控apache服务器进程脚本分享
2014/01/15 Python
python中bisect模块用法实例
2014/09/25 Python
python实现比较两段文本不同之处的方法
2015/05/30 Python
Python基于高斯消元法计算线性方程组示例
2018/01/17 Python
Linux(Redhat)安装python3.6虚拟环境(推荐)
2018/05/05 Python
对Python3+gdal 读取tiff格式数据的实例讲解
2018/12/04 Python
pytorch获取模型某一层参数名及参数值方式
2019/12/30 Python
django haystack实现全文检索的示例代码
2020/06/24 Python
使用PyCharm安装pytest及requests的问题
2020/07/31 Python
卡西欧G-SHOCK英国官网: 防水防震手表
2018/01/08 全球购物
Kipling澳洲官网:购买凯浦林包包
2020/12/17 全球购物
说出一些常用的类,包,接口
2014/09/22 面试题
对祖国的寄语大全
2014/04/11 职场文书
2015年毕业生自我鉴定模板
2014/09/19 职场文书
学校三八妇女节活动总结
2015/02/06 职场文书
灵魂歌王观后感
2015/06/17 职场文书
党风廉洁教育心得体会
2016/01/20 职场文书