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
Jan 24 NodeJs
详谈Angular路由与Nodejs路由的区别
Mar 05 NodeJs
nodeJS(express4.x)+vue(vue-cli)构建前后端分离实例(带跨域)
Jul 05 NodeJs
NodeJs通过async/await处理异步的方法
Oct 09 NodeJs
详解NODEJS基于FFMPEG视频推流测试
Nov 17 NodeJs
nodejs 最新版安装npm 的使用详解
Jan 18 NodeJs
webstorm中配置nodejs环境及npm的实例
May 15 NodeJs
Nodejs实现多文件夹文件同步
Oct 17 NodeJs
NodeJS读取分析Nginx错误日志的方法
May 14 NodeJs
Nodejs 数组的队列以及forEach的应用详解
Feb 25 NodeJs
浅谈Node的内存泄露问题
May 06 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生成带有雪花背景的验证码
2006/10/09 PHP
php使用curl访问https示例分享
2014/01/17 PHP
php简单判断两个字符串是否相等的方法
2015/07/13 PHP
PHP版本常用的排序算法汇总
2015/12/20 PHP
PHP的介绍以及优势详细分析
2019/09/05 PHP
用YUI做了个标签浏览效果
2007/02/20 Javascript
JQuery UI皮肤定制
2009/07/27 Javascript
jquery多浏览器捕捉回车事件代码
2010/06/22 Javascript
9行javascript代码获取QQ群成员具体实现
2013/10/16 Javascript
Nodejs全栈框架StrongLoop推荐
2014/11/09 NodeJs
express的中间件basicAuth详解
2014/12/04 Javascript
JS读取XML文件数据并以table形式显示数据的方法(兼容IE与火狐)
2016/06/02 Javascript
使用jQuery的ajax方法向服务器发出get和post请求的方法
2017/01/13 Javascript
JS变量及其作用域
2017/03/29 Javascript
bootstrap confirmation按钮提示组件使用详解
2017/08/22 Javascript
nodejs发送http请求时遇到404长时间未响应的解决方法
2017/12/10 NodeJs
webpack4手动搭建Vue开发环境实现todoList项目的方法
2019/05/16 Javascript
python自动格式化json文件的方法
2015/03/11 Python
如何使用 Pylint 来规范 Python 代码风格(来自IBM)
2018/04/06 Python
Python走楼梯问题解决方法示例
2018/07/25 Python
解决pandas .to_excel不覆盖已有sheet的问题
2018/12/10 Python
PyQt5的安装配置过程,将ui文件转为py文件后显示窗口的实例
2019/06/19 Python
Python实现平行坐标图的两种方法小结
2019/07/04 Python
python实现基于朴素贝叶斯的垃圾分类算法
2019/07/09 Python
Python实现发票自动校核微信机器人的方法
2020/05/22 Python
解决Pytorch自定义层出现多Variable共享内存错误问题
2020/06/28 Python
利用Python实现字幕挂载(把字幕文件与视频合并)思路详解
2020/10/21 Python
英国家庭和商业健身器材购物网站:Fitness Options
2018/07/05 全球购物
南京某公司笔试题
2013/01/27 面试题
村委会贫困证明范文
2014/09/21 职场文书
健康教育主题班会
2015/08/14 职场文书
小学生红领巾广播稿
2015/08/19 职场文书
2019年汽车租赁合同范本!
2019/08/12 职场文书
Redis持久化与主从复制的实践
2021/04/27 Redis
手把手教你怎么用Python实现zip文件密码的破解
2021/05/27 Python
JavaWeb Servlet实现网页登录功能
2021/07/04 Java/Android