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中的非阻塞方法介绍
Jun 05 NodeJs
Nodejs+express+html5 实现拖拽上传
Aug 08 NodeJs
Nodejs Post请求报socket hang up错误的解决办法
Sep 25 NodeJs
基于NodeJS的前后端分离的思考与实践(五)多终端适配
Sep 26 NodeJs
NodeJS学习笔记之Connect中间件模块(一)
Jan 27 NodeJs
NodeJS Web应用监听sock文件实例
Feb 18 NodeJs
NodeJs的优势和适合开发的程序
Aug 14 NodeJs
NodeJs的fs读写删除移动监听
Apr 28 NodeJs
nodejs6下使用koa2框架实例
May 18 NodeJs
Nodejs 复制文件/文件夹的方法
Aug 24 NodeJs
nodejs基于WS模块实现WebSocket聊天功能的方法
Jan 12 NodeJs
Nodejs + Websocket 指定发送及群聊的实现
Jan 09 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中url传递中文字符,特殊危险字符的解决方法
2013/08/17 PHP
smarty模板引擎之配置文件数据和保留数据
2015/03/30 PHP
php+ajax实现无刷新文件上传功能(ajaxuploadfile)
2018/02/11 PHP
jQuery(1.3.2) 7行代码搞定跟随屏幕滚动的层
2009/05/21 Javascript
EXTjs4.0的store的findRecord的BUG演示代码
2013/06/08 Javascript
通过javascript把图片转化为字符画
2013/10/24 Javascript
JavaScript创建闭包的两种方式的优劣与区别分析
2015/06/22 Javascript
js document.getElementsByClassName的使用介绍与自定义函数
2016/11/25 Javascript
js实现手机发送验证码功能
2017/03/13 Javascript
js按条件生成随机json:randomjson实现方法
2017/04/07 Javascript
基于jQuery实现图片推拉门动画效果的两种方法
2017/08/26 jQuery
jQuery实现checkbox即点即改批量删除及中间遇到的坑
2017/11/11 jQuery
基于JavaScript 性能优化技巧心得(分享)
2017/12/11 Javascript
Vue包大小优化的实现(从1.72M到94K)
2021/02/18 Vue.js
Python中map,reduce,filter和sorted函数的使用方法
2015/08/17 Python
python判断一个集合是否为另一个集合的子集方法
2018/05/04 Python
python+pyqt5编写md5生成器
2019/03/18 Python
从0开始的Python学习016异常
2019/04/08 Python
Python OpenCV调用摄像头检测人脸并截图
2020/08/20 Python
python global和nonlocal用法解析
2020/02/03 Python
keras分类模型中的输入数据与标签的维度实例
2020/07/03 Python
python实现简单遗传算法
2020/09/18 Python
Pandas替换及部分替换(replace)实现流程详解
2020/10/12 Python
CSS3中的clip-path使用攻略
2015/08/03 HTML / CSS
CSS3中引入多种自定义字体font-face
2020/06/12 HTML / CSS
瑞士国际航空官网:SWISS
2016/07/21 全球购物
阿迪达斯荷兰官方网站:adidas荷兰
2018/03/16 全球购物
小米官方旗舰店:Xiaomi
2020/08/07 全球购物
《草虫的村落》教学反思
2014/02/16 职场文书
安全生产目标管理责任书
2014/07/25 职场文书
党员自我评议个人对照检查材料
2014/09/16 职场文书
2015年度个人工作总结报告
2015/10/24 职场文书
超市啤酒狂欢夜策划方案范文!
2019/07/03 职场文书
SQLServer2019 数据库环境搭建与使用的实现
2021/04/08 SQL Server
python实现A*寻路算法
2021/06/13 Python
JavaGUI模仿QQ聊天功能完整版
2021/07/04 Java/Android