依赖注入解释
A作为提供方将自己注入至代理方,然后B作为消费方从代理中获取提供方信息
工具使用
1. 使用TS语法自带的装饰器进行语法糖包装
2. 使用reflect-metadata来获取原类型的映射
3.关于reflect-metadata的高阶用法,强烈推荐阅读【掌握 JS 高级编程基础】
代码实现思路:
- 定义元数据类说明
//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