从0开发属于自己的nestjs框架的mini 版 —— Module篇
在开写之前,我们看一下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的终极版