从0开发属于自己的nestjs框架的mini 版 —— Module篇

发布时间 2023-07-30 16:41:47作者: 天高任鸟飞吧

在开写之前,我们看一下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<T = any> {
  imports?: Array<Type<T>>;
  providers?: (Provider<T> | Type<T>)[]; //Array<Provider<T>|Type<T>>
  controllers?: Type<T>[];
}


模块的装饰器实现


/**
 * 定义模块的装饰器
 * @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<K> {
  private iocInstance: Container;
  constructor(entryModule?: Type<K>) {
    this.iocInstance = new Container();
    entryModule && this.init(entryModule);
  }
  
  init<T>(entryModule: Type<T>) {
    this.bindModule(entryModule);
    this.iocInstance.loading();
  }

  /**
   *
   * @returns 获取标识为控制器的实例
   */
  public getControllerInstance() {
    let result: Type<any>[] = [];
    let allInstance = this.iocInstance.getInstance();
    allInstance.forEach((value: Type<any>, key) => {
      if (
        typeof key === "function" &&
        Reflect.getMetadata("IS_Controller_KEY", key)
      ) {
        result.push(value);
      }
    });
    return result;
  }

  //模块绑定
  public bindModule<T>(module: Type<T>) {
    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<T>(provider: ClassProvider<T>) {
    let meataData = Reflect.getMetadata(
      Module_Metate_Params,
      provider.useClass
    );
    if (!meataData) return;
    /**
     * 加载普通的provider
     */
    if (Array.isArray(meataData.providers)) {
      meataData.providers.forEach((item: ClassProvider<T>) =>
        this.iocInstance.add(item)
      );
    }

    /**
     * 加载标识为控制器的provider
     */
    this.bindModuleLoadControllers(meataData.controllers || []);
    /**
     * 加载标识为模块的provider
     */
    if (Array.isArray(meataData.imports)) {
      meataData.imports.forEach((itme: Type<T>) => this.bindModule(itme));
    }
  }

  /**
   * 控制器注解,特殊的标记的provider
   * @param providers
   */
  private bindModuleLoadControllers<T>(providers: Type<T>[]) {
    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<Type<any>> = m.getControllerInstance();

// console.log("控制器实例:", controllers);
controllers.forEach((value, key) => {
  console.log("控制器实例:", value, "---", key);
});

let c: Type<C> = controllers[0];
console.log("控制器实例c:", c, c.getC());


总结

1、ContainerModule 其实还是一个ioc ,只是通过它来进行加在不同类型的服务和递归加载模块容器
2、Module 上的 controllers 参数并没有和路由实现相关的东西,本质就是一个provide 提供服务类
3、下一篇,我们将两者结合起来实现最终的nestjs的终极版