TS依赖注入简单实现举例

依赖注入解释

A作为提供方将自己注入至代理方,然后B作为消费方从代理中获取提供方信息


工具使用

1.​ 使用TS语法自带的装饰器进行语法糖包装
2. 使用reflect-metadata来获取原类型的映射
3.关于reflect-metadata的高阶用法,强烈推荐阅读【掌握 JS 高级编程基础


代码实现思路:

  1. 定义元数据类说明
//MetaData.ts
export const MetaData = {
	CONTAINERS: Symbol("containers"), //容器集合
	InjectionUniqueKey: Symbol("injection_uniqueKey"), //注入时用到的值
	paramTypes: "design:paramtypes", //传参类型  以下三个基于reflect-metaData的
	returnType: "design:returntype", //返回类型
	designType: "design:type", //设计类型
	TARGET: Symbol("__target__"),
};

2.定义提供方注入依赖

//InjectionDependency.ts
import { MetaData } from "./MetaData";
import * as crypto from "crypto";

/***
 * @version 1.0 注入依赖
 */
export default function InjectionDependency(alias?: string) {
	return function (target: any) {
		const proxy = new Proxy(target, {
			construct: (target: any, args: any) => {
				let service = new target(...args);

				let name = target.name || "injection";
				let key = `${name}:${crypto.randomBytes(16).toString("hex")}`;

				Reflect.defineMetadata(MetaData.InjectionUniqueKey, key, target); //放入至原型中

				//注入至容器内
				let CONTAINERS: Map<string, any> = Reflect.getMetadata(MetaData.CONTAINERS, global);

				if (!CONTAINERS) {
					CONTAINERS = new Map<string, any>();
					Reflect.defineMetadata(MetaData.CONTAINERS, CONTAINERS, global);
				}

				CONTAINERS.set(key, service);
				if (!!alias) {
					CONTAINERS.set(alias, service);
				}

				return service;
			},
		});

		//增加一个获取原对象的方法
		proxy[MetaData.TARGET] = target;
		return proxy;
	};
}

3.实现消费方获取依赖

//CallDependency.ts
import "reflect-metadata";

import { MetaData } from "./MetaData";

const SpecWords = ["Boolean", "Number", "String", "Object"];

function getNameByPropertyKey(target: any, propertyKey: string): string {
	let designType = Reflect.getMetadata(MetaData.designType, target, propertyKey);
	let key = "";
	let name = "";
	if (designType) {
		name = designType.name;
		if (Reflect.has(designType, MetaData.TARGET)) {
			designType = Reflect.get(designType, MetaData.TARGET);
		}
		key = Reflect.getMetadata(MetaData.InjectionUniqueKey, designType); //放入至原型中
	}
	//获取不到注入的值时默认为别名的值
	if (!name || SpecWords.includes(name)) {
		key = propertyKey;
	}

	return key;
}

/***
 * @version 1.0 调用依赖
 *
 */
export default function CallDependency(target: any, propertyKey: string) {
	Reflect.defineProperty(target, propertyKey, {
		get: () => {
			let key = getNameByPropertyKey(target, propertyKey);
			let CONTAINERS = Reflect.getMetadata(MetaData.CONTAINERS, global);

			if (!CONTAINERS) {
				return null;
			}

			return CONTAINERS.get(key) || null;
		},
	});
}

调用测试:

import "reflect-metadata";
import InjectionDependency from "../src/InjectionDependency";
import CallDependency from "../src/CallDependency";
import { MetaData } from "../src/MetaData";

@InjectionDependency()
class TestInjection {
	sayHello() {
		return "hello";
	}
}

class TestCall {
	@CallDependency
	testInjection!: TestInjection;
}

@InjectionDependency("alias")
class TestInjectionAlias {
	sayHello() {
		return "world";
	}
}

describe("注入依赖测试", () => {
	it("注入和依赖获取", () => {
		//先实例化提供方
		let provide = new TestInjection();
		//再实例化调用放进行获取
		let call = new TestCall();
		console.log(call.testInjection.sayHello() == "hello");
		console.log(call.testInjection == provide);

		//别名获取
		let aliasProvide = new TestInjectionAlias();
		console.log(Reflect.getMetadata(MetaData.CONTAINERS, global).get("alias") == aliasProvide);
	});
});

使用场景说明

当我们需要调用一个对象使用时,我们通常需要进行导入,然后赋值后再进行使用。而依赖注入可以通过中间代理的管理,然后通过装饰器的语法糖,在调用该对象时,进行拦截获取。


有哪些框架在使用依赖注入的原理

  • 基于JAVA的spring框架
  • 基于TS的nest.ts框架
  • …欢迎补充

示例源代码仓库地址

https://william_zhong.coding.net/public/node-demo/reflect-injection/git/files

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值