项目架构(2)-- 依赖库【reflect-metadata】【inversify】【inversify-express-utils】
之前的文章已经学习了
我们需要再学习:
inversify
+ reflect-metadata
实现依赖注入 官网
连接工具 inversify-express-utils 官网
reflect-metadata
是一个 JavaScript 库,用于在运行时存储和读取元数据。它为 JavaScript 提供了反射功能,允许开发者在运行时获取关于对象、类、方法等的附加元数据。这个库特别在 TypeScript 中与装饰器(decorators)一起使用,来支持反射机制,从而增强代码的灵活性和可扩展性。
主要功能
- 元数据存储:reflect-metadata 允许你在类、方法、属性、参数等上存储元数据。这些元数据可以包含类型信息、方法信息、配置参数等,且可以在运行时通过反射读取。
- 反射功能:提供了读取和操作元数据的 API,允许开发者访问已经附加到类或对象上的元数据。这种反射机制对于依赖注入、自动验证等高级功能非常重要。
- 支持装饰器:与 TypeScript 的装饰器结合使用时,reflect-metadata 允许装饰器在运行时访问类或方法的类型信息及其他元数据,使得框架(如 Angular、NestJS)能够实现依赖注入、路由处理等功能。
主要功能 API
1.Reflect.defineMetadata
用于在对象或方法上定义元数据。
import "reflect-metadata"; class Example { @Reflect.metadata("key", "value") myMethod() {} }
2.Reflect.getMetadata
用于从对象或方法中获取元数据。
const metadata = Reflect.getMetadata("key", Example.prototype, "myMethod"); console.log(metadata); // 输出: "value"
3.Reflect.hasMetadata
用于检查对象或方法上是否有特定的元数据。
const hasMeta = Reflect.hasMetadata("key", Example.prototype, "myMethod"); console.log(hasMeta); // 输出: true
4.Reflect.deleteMetadata
用于删除对象或方法上的元数据。
Reflect.deleteMetadata("key", Example.prototype, "myMethod");
工作原理
- reflect-metadata 在运行时将元数据附加到目标对象上,允许开发者通过反射访问这些元数据。
- 它本身并不会影响 TypeScript 的编译过程,但结合 emitDecoratorMetadata 选项一起使用时,可以通过装饰器访问类型信息。
使用场景
依赖注入:许多框架(例如 Angular、NestJS)使用装饰器标记类或方法的元数据,反射这些元数据并自动注入依赖。
import "reflect-metadata"; function Inject(token: any) { return function(target: any, key: string | symbol) { Reflect.defineMetadata("design:type", token, target, key); }; } class UserService { constructor() {} } class MyController { @Inject(UserService) userService: UserService; }
路由和权限验证:框架可以通过装饰器为类或方法定义路由、权限要求等元数据,并在运行时根据这些元数据来执行操作。
验证与约束:通过装饰器和反射,框架能够自动验证数据模型或函数的参数类型,进行类型检查或其他验证。
日志记录与方法拦截:通过反射元数据,可以实现方法的自动记录或拦截功能(比如执行时间、参数、返回值等)。
InversifyJS
是一个轻量级的 依赖注入(Dependency Injection, DI) 库,专门用于在 TypeScript 和 JavaScript 应用中实现 依赖注入模式。它主要用于帮助开发者更容易地管理对象的依赖关系,提高代码的可维护性、可测试性和解耦性。
核心概念:
-
Container(容器):
- 容器是 InversifyJS 的核心,它负责存储和管理依赖项,并通过容器来解决对象的创建和依赖注入。
-
@injectable():
- 这个装饰器用于标记一个类可以被 Inversify 容器管理和注入依赖。
-
@inject():
- 这个装饰器用于在构造函数中标记需要注入的依赖对象。
-
bind():
- 用于将一个类或接口与其实现绑定到容器中,以便容器能够注入依赖。
import { injectable, inject, Container } from "inversify"; // 定义接口 interface IGreeter { greet(): string; } // 实现接口的类 @injectable() class Greeter implements IGreeter { greet() { return "Hello, world!"; } } // 需要注入 Greeter 服务的类 @injectable() class User { private greeter: IGreeter; // 通过构造函数注入 Greeter 实例 constructor(@inject(Greeter) greeter: IGreeter) { this.greeter = greeter; } greetUser() { console.log(this.greeter.greet()); } } // 创建容器 const container = new Container(); // 注册 Greeter 和 User 到容器中 container.bind<IGreeter>(Greeter).to(Greeter); container.bind<User>(User).to(User); // 从容器中获取 User 实例 const user = container.get<User>(User); user.greetUser(); // 输出:Hello, world!
inversify-express-utils
是一个基于 InversifyJS 的库,它提供了一些工具和功能,使得你可以轻松地将 InversifyJS 与 Express 框架结合使用,来构建依赖注入驱动的 RESTful API 应用。
主要功能:
- 自动依赖注入:通过与 InversifyJS 配合使用,
inversify-express-utils
允许你在 Express 路由中使用构造函数注入,而不需要手动管理依赖。它会自动将相关依赖注入到控制器中,使得代码更加简洁和易于维护。 - 创建 RESTful 路由:它为你提供了一个简单的方式来定义 Express 路由,通过装饰器来处理 HTTP 请求,并将请求处理与控制器方法关联起来。
- 控制器装饰器:通过
@controller
和@httpGet
等装饰器,inversify-express-utils
允许你使用声明式的方式来定义路由和对应的处理方法。这大大减少了传统 Express 应用中冗长的路由配置。
主要特性:
- @controller:标记类为控制器类,表示该类将处理 HTTP 请求。
- 路由装饰器:使用
@httpGet
,@httpPost
,@httpPut
等装饰器来简化 HTTP 请求和控制器方法之间的映射。 - 依赖注入:依赖注入机制允许你将服务和组件注入到控制器中,减少了硬编码的依赖。
示例代码
使用 inversify-express-utils
构建一个 RESTful API:
项目目录:
0.初始化
npm init -y
tsc --init
打开ts配置
1.安装 inversify
, inversify-express-utils
, express
等依赖
npm install inversify inversify-express-utils reflect-metadata
2.定义服务 /greeter/service.ts
import { injectable } from "inversify"; @injectable() export class GreeterService { greet(): string { return "Hello from GreeterService!"; } }
3.创建控制器 /greeter/controller.ts
import { controller, httpGet } from "inversify-express-utils"; import { inject } from "inversify";
import { GreeterService } from "./service";
@controller("/greet") export class GreeterController { constructor(@inject(GreeterService) private greeterService: GreeterService) {} @httpGet("/") public greet() { return this.greeterService.greet(); } }
import "reflect-metadata"; // Reflect Metadata 是必需的 import { Container } from "inversify"; import { InversifyExpressServer } from "inversify-express-utils";
import { GreeterService } from "./greeter/service";
import { GreeterController } from "./greeter/controller";
// 设置容器 const container = new Container(); container.bind(GreeterService).toSelf(); container.bind(GreeterController).toSelf(); // 创建 Express 服务器并配置 Inversify 容器 const server = new InversifyExpressServer(container); // 启动服务器 server.build().listen(3000, () => { console.log("Server is running on http://localhost:3000"); });
http://localhost:3000/greet ,你应该会看到:
