【资料图】
在开写之前,我们看一下nestjs 关于Module 装饰器的用法:
- 有四个参数,每个参数都是一个数组,controllers控制器,主要是路由的providers 提供给该模块用的服务imports导入的其他模块的服务或者模块exports 导出该模块中的服务
import { Module } from "@nestjs/common";@Module({ controllers: [], providers: [], imports:[], exports:[],})export class AppModule {}
不出意外的无意外,我们将实现该模块的功能,在这里我们只需要三个参数即何,分别是 controllers,providers,imports, 其关键一步就是我们将结合第一篇的ioc来实现
话不多说,上代码
src\ioc-module.ts
引入ioc 核心(ioc篇实现的)
import { Container, ClassProvider, InjectableKey, Provider, Type,} from "./ioc-core";
定义常量
// 标识模块export const IS_Module_KEY = "IS_Module_KEY";// 标识模块注入的参数export const Module_Metate_Params = "Module_Metate_Params";
声明参数类型
/** * 声明模块的参数类型 */export interface Imodule { imports?: Array>; providers?: (Provider | Type)[]; //Array|Type> controllers?: Type[];}
模块的装饰器实现
/** * 定义模块的装饰器 * @param option * @returns */export function Module(option?: Imodule) { return function (target: any) { Reflect.defineMetadata(IS_Module_KEY, true, target); Reflect.defineMetadata(Module_Metate_Params, option, target); return target; };}
模块IOC 的容器
/** * 模块ioc的容器 */export class ContainerModule { private iocInstance: Container; constructor(entryModule?: Type) { this.iocInstance = new Container(); entryModule && this.init(entryModule); } init(entryModule: Type) { this.bindModule(entryModule); this.iocInstance.loading(); } /** * * @returns 获取标识为控制器的实例 */ public getControllerInstance() { let result: Type[] = []; let allInstance = this.iocInstance.getInstance(); allInstance.forEach((value: Type, key) => { if ( typeof key === "function" && Reflect.getMetadata("IS_Controller_KEY", key) ) { result.push(value); } }); return result; } //模块绑定 public bindModule(module: Type) { if (!Reflect.getMetadata(IS_Module_KEY, module)) { console.log("导入的imports参数不属于模块"); return; } const provider = { provide: module, useClass: module }; Reflect.defineMetadata(InjectableKey, true, module); this.iocInstance.add(provider); this.bindLoadModule(provider); } private bindLoadModule(provider: ClassProvider) { let meataData = Reflect.getMetadata( Module_Metate_Params, provider.useClass ); if (!meataData) return; /** * 加载普通的provider */ if (Array.isArray(meataData.providers)) { meataData.providers.forEach((item: ClassProvider) => this.iocInstance.add(item) ); } /** * 加载标识为控制器的provider */ this.bindModuleLoadControllers(meataData.controllers || []); /** * 加载标识为模块的provider */ if (Array.isArray(meataData.imports)) { meataData.imports.forEach((itme: Type) => this.bindModule(itme)); } } /** * 控制器注解,特殊的标记的provider * @param providers */ private bindModuleLoadControllers(providers: Type[]) { if (!Array.isArray(providers)) { return; } providers.forEach((itme) => { Reflect.defineMetadata("IS_Controller_KEY", true, itme); //标志位控制器 Reflect.defineMetadata(InjectableKey, true, itme); //标志为可注入依赖 this.iocInstance.add({ provide: itme, useClass: itme }); }); }}
测试用例
import { Inject, Injectable } from "./ioc-core";import { ContainerModule, Module } from "./ioc-module";import { Type } from "./util";@Injectable()class A { constructor(@Inject("api") private api: string /** b:number **/) { console.log("----实例化A:"); console.log("a-api", this.api); } getA() { console.log("执行到A 类的 getA 方法"); return this.api; }}@Injectable()class B { constructor(@Inject("AA") private a: A, @Inject("api") private api: string) { console.log("----实例化B:"); console.log("B:insA", this.a); console.log("B:api", this.api); } getB() { return this.a.getA(); }}@Injectable()class C { constructor(private b: B, @Inject("api") private api: string) { console.log("----实例化C:"); console.log("C:insB", this.b); console.log("C:api", this.api); } getC() { return this.b.getB(); }}@Module({ providers: [ A, B, { provide: "AA", useClass: A }, { provide: "api", useValue: 123 }, ], controllers: [C], imports:[]})class M {}const m = new ContainerModule(M);let controllers: Array> = m.getControllerInstance();// console.log("控制器实例:", controllers);controllers.forEach((value, key) => { console.log("控制器实例:", value, "---", key);});let c: Type = controllers[0];console.log("控制器实例c:", c, c.getC());
总结
1、ContainerModule 其实还是一个ioc ,只是通过它来进行加在不同类型的服务和递归加载模块容器2、Module 上的 controllers 参数并没有和路由实现相关的东西,本质就是一个provide 提供服务类3、下一篇,我们将两者结合起来实现最终的nestjs的终极版
关键词: