依赖注入通常也是我们所说的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()// 执行

  

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

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

typescript nodejs 依赖注入实现的更多相关文章

  1. 使用Typescript实现依赖注入(DI)

    前言DI总是和ico相辅相成的,如果想对DI有更多的了解,可以移步我的另一篇文章 依赖注入(DI)和控制反转(IOC),再次我就不多做赘述了. 前几天看见一道面试题,今天借这个话题想跟大家分享一下: ...

  2. typedi 强大的javascript以及typescript 依赖注入框架

    typedi 是typestack团队提供的依赖注入解决方案,对于typescript 我们可以使用注解的开发方式,官方的文档也比较详细 javascript 使用 基于函数的服务注入 var Ser ...

  3. 基于nodejs的流水线式的CRUD服务。依赖注入可以支持插件。

    写代码好多年了,发现大家的思路都是写代码.写代码.写代码,还弄了个称号——码农. 我是挺无语的,我的思路是——不写代码.不写代码.不写代码! 无聊的代码为啥要重复写呢?甚至一写写好几年. 举个例子吧, ...

  4. 用Decorator实现依赖注入,像Java一样写后台

    最近闲来无事,突发奇想,也顺便练练手,于是就萌生了,能否用typescript的decorator写一个Nodejs SpringMVC,通过依赖注入,自动实现文件加载,实例化等.然后就有了这个项目. ...

  5. Angular2 依赖注入

    1. 使用DI 依赖注入是一个很重要的程序设计模式. Angular 有自己的依赖注入框架,离开了它,我们几乎没法构建 Angular 应用.它使用得非常广泛,以至于几乎每个人都会把它简称为 DI. ...

  6. AngularJS学习笔记之依赖注入

    最近在看AngularJS权威指南,由于各种各样的原因(主要是因为我没有money,好讨厌的有木有......),于是我选择了网上下载电子版的(因为它不要钱,哈哈...),字体也蛮清晰的,总体效果还不 ...

  7. 细数Javascript技术栈中的四种依赖注入

    作为面向对象编程中实现控制反转(Inversion of Control,下文称IoC)最常见的技术手段之一,依赖注入(Dependency Injection,下文称DI)可谓在OOP编程中大行其道 ...

  8. angular2 学习笔记 ( DI 依赖注入 )

    refer : http://blog.thoughtram.io/angular/2016/09/15/angular-2-final-is-out.html ( search Dependency ...

  9. 迈向angularjs2系列(5):依赖注入

    一: 为什么要依赖注入 1.构造器引入依赖 假设一个类Car类依赖于Engine(引擎)类.Transition(变速箱)类,可使用构造器来完成. //类似如下代码 class Engine{} cl ...

随机推荐

  1. canal中间件

    简介: 基于数据库增量(模拟MySQL slave的交互协议)日志解析,提供增量数据订阅和消费(客户端与canal建立关系) 安装版本:1.1.0 git 环境需求: jdk1.7以上 mysql开启 ...

  2. Linux的DNS主从服务器部署

    下面的部署是在Linux的DNS正向解析部署上进行修改的. 如果有什么问题或者错误,可以访问上篇帖子 下面开始有关DNS的服务部署.<DNS主从服务器> 环境描述: 192.168.196 ...

  3. Flask中before_request与after_request使用

    目录 1.前提,装饰器的弊端 2.before_request与after_request 2.1 before_request分析: 2.2 after_request分析: 3.before_re ...

  4. mongo find 时间条件过滤

    db.order.find({"order_time":{"$gte": new Date("Tue Jan 01 2017 00:00:00 GMT ...

  5. 中国网络安全行业分类及全景图2019H1

    中国网络安全行业分类及全景图2019H1   概述 中国网络安全行业分类及全景图: 一级分类包含了端点安全.网络安全.应用安全.数据安全.身份与访问管理和安全管理六个一级分类,这些一级分类分别对应了网 ...

  6. 前端(4)BOM与DOM

    前端(4)BOM与DOM I/O前戏 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些交 ...

  7. 查看linux服务器配置信息命令

    查看 cpu信息: cat /proc/cpuinfo 查看内存信息: grep MemTotal /proc/meminfo 查看操作系统信息: uname -a 查看centos版本信息: cat ...

  8. pycharm运行过程中pycharm控制台和python控制台之间的切换

    有时候在调试(debug)python代码的时候,希望像matlab那样输入某一个变量以查看其值,这是需要把pycharm的console切换到python的console,以输入变量查看其值,点击那 ...

  9. [KCOJ20170214]又一个背包

    题目描述 Description 小W要去军训了!由于军训基地是封闭的,小W在军训期间将无法离开军训基地.所以他没有办法出去买他最爱吃的零食.万般无奈的小W只好事先买好他爱吃的零食,装在背包里带入军训 ...

  10. 07_LACP负载分担(数通华为)

    1. 网络拓扑 2. SW1配置:[SW1]vlan batch 10 20[SW1]interface GigabitEthernet 0/0/1[SW1-GigabitEthernet0/0/1] ...