用 typescript 实现依赖注入模式 本篇文章将介绍如何使用 typescript 来实现依赖注入功能,并考虑到各种情况,包括实现单例和处理循环依赖等
概念 依赖注入的概念非常简单。它基于一个容器(Container),用于管理对象的创建和解决依赖关系。在使用依赖注入之前,我们需要定义一些“可注入”的类(Injectable),并使用“Inject”标记它们的依赖关系。现在,让我们来逐步实现这个过程。
实现 Step.1: 创建 @injectable 装饰器 @injectable 可以将一个类标记为一个可注入类, 可以使用 reflect-metadata 库来实现这个装饰器
首先, 安装 reflect-metadata 库
之后在引入这个库并定义 injectable 函数
1 2 3 4 5 6 7 import "reflect-metadata" ;export function Injectable ( ) { return function (taget : any ) { Reflect .defineMetadata ("injectable" , true , target); }; }
这个装饰器使用 Reflect.defineMetadata 为被装饰的类添加了一个 injectable 的元数据标识
step.2: 创建 @inject 装饰器 @inject 装饰器用于标记类的依赖关系
1 2 3 4 5 6 7 8 import "reflect-metadata" ;export function Inject ( ) { return function (target : any , propertyKey : string ) { const type = Reflect .getMetadata ("design:type" , target, propertyKey); Reflect .defineMetadata ("inject" , type , target, propertyKey); }; }
这个装饰器使用 Reflect.getMetadata 获取标记属性的类型, 随后用 Reflect.defineMetadata 将类型信息储存到元数据中
step.3: 实现容器类 容器类 Container 用于管理类的实例的创建和解决依赖关系, 需要支持以下功能:
注册标记为 @injectable 的类
处理依赖关系
创建单例
解决循环依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import "reflect-metadata" ;class Container { private instances : Map <Constructor , any > = new Map (); private resolvingQueue : Set <Constructor > = new Set (); register<T>(cls : Constructor <T>) { const injectable = Reflect .getMetadata ("injectable" , cls); if (!injectable) { throw new Error (`Class ${cls.name} is not marked with @injectable` ); } this .instances .set (cls, null ); } resolve<T>(cls : Constructor <T>): T { if (this .resolvingQueue .has (cls)) { throw new Error (`Circular dependency detected for class ${cls.name} ` ); } const instance = this .instances .get (cls); if (instance !== null ) { return instance; } this .resolvingQueue .add (cls); const injectParams = Reflect .getMetadata ("design:paramtypes" , cls) || []; const resolvedParams = injectParams.map ((param ) => this .resolve (param)); const newInstance = new cls (...resolvedParams); this .instances .set (cls, newInstance); this .resolvingQueue .delete (cls); return newInstance; } }
这个 Container 类有两个主要方法: register 用于注册可注入的类, resolve 用于处理依赖关系, 并且维护单例和检查循环依赖
示例 将我们的依赖注入模块作为一个库引入, 来写一个简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { Injectable , Inject , Container } from "my-di-lib" ;@Injectable ()class Foo { constructor (@Inject () public bar : Bar ) {} } @Injectable ()class Bar {}const container = new Container ();container.register (Foo ); container.register (Bar ); const foo = container.resolve (Foo );foo instanceof Foo ; foo.bar instanceof Bar ;