依赖注入通常也是我们所说的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. python使用sched模块执行周期性任务和定时任务

    执行周期性任务 sched模块是一个通用的事件调度程序,可以对任务进行延迟调度,基于此,可以用它来实现周期性任务. # coding:utf8 import time import sched # 初 ...

  2. 《linux就该这么学》课堂笔记17 分离解析、DHCP、电子邮件系统

    1.动态主机配置协议(DHCP,Dynamic Host Configuration Protocol) 自动管理局域网内主机的IP地址.子网掩码.网关地址及DNS地址等参数,可以有效地提升IP地址的 ...

  3. rhel7学习第五天

    管道命令符的功能的确强大!

  4. IDEA中如何导入一个maven项目并配置相关设置

    导入一个maven项目参照如下链接 https://jingyan.baidu.com/article/b0b63dbf0c0ac04a49307078.html 要想启动这个导入的项目目前我所接触到 ...

  5. boost与MFC的冲突(new)

    在MFC对话框程序中用boost::signals2时出现了问题, 由于MFC为了方便调试,在debug下重新定义了new #ifdef _DEBUG#define new DEBUG_NEW#end ...

  6. Spring Boot 注入外部配置到应用内部

    Spring Boot允许你外部化你的配置,这样你就可以在不同的环境中使用相同的应用程序代码,你可以使用properties文件.YAML文件.环境变量和命令行参数来外部化配置,属性值可以通过使用@V ...

  7. serialize()和new FormData()的区别

    serialize()和FormData对象都可将表单数据序列化,后通过ajax异步提交 ,序列化表单就是将form表单中所有name属性序列化成KEY-VALUE的形式,提交到后台,后台以对象相应的 ...

  8. [RN] React Native 使用 阿里 ant-design

    React Native 使用 阿里 ant-design 实例效果如图: 一.安装 npm install antd-mobile-rn --save npm install babel-plugi ...

  9. [东西]EquationCalcular

    名称:EquationCalcular 版本:V1.0.0 更新日期:2015/9/27   简要介绍:本工具用于计算范围比较有限的方程及方程组,仅仅局限于n元一次方程组,欢迎需要的小学生和初中生来玩 ...

  10. ACE在Ubuntu下的安装和编译

    之前写了很多linux下的底层网络API的demo,这些demo可用于了解底层的网络通信过程,但是想做出好的服务器用于实际业务还是非常困难的,需要大量的代码实现,移植性也非常差,想要写出高性能架构的服 ...