1. 前言
我们可以在 midway控制器 篇中可以看到 路由方法 提供的路由方法装饰器 代码如下
// src/controller/home.tsimport { Controller, Get, Provide } from '@midwayjs/decorator';@Provide()@Controller('/')export class HomeController {@Get('/')async home() {return 'Hello Midwayjs!';}@Post('/update')async updateData() {return 'This is a post method'}}
Midway 还提供了其他的装饰器, @Get 、 @Post 、 @Put() 、 @Del() 、 @Patch() 、 @Options() 、 @Head() 和 @All() ,表示各自的 HTTP 请求方法。
以上全部摘抄自官方文档 然后按照 从 @midwayjs/decorator 从 来分析 Controller 以及上面描述的这些HTTP方法
2. Controller
首先 我们看到的 整个 controller的控制器
在 midway 中源码如下
export function Controller(prefix = '/',routerOptions: {sensitive?: boolean;middleware?: MiddlewareParamArray;description?: string;tagName?: string;} = { middleware: [], sensitive: true }): ClassDecorator {return (target: any) => {//saveModule(CONTROLLER_KEY, target);if (prefix)saveClassMetadata(CONTROLLER_KEY,{prefix,routerOptions,} as ControllerOption,target);Scope(ScopeEnum.Request)(target);Provide()(target);};}
首先 我们可以想到的是 controller 在使用上 是一个类装饰器
它的入参 是 prefix 和 routerOptions
一般我们在使用的时候 最多就是 设置一个 prefix
这些都很容易理解
接着我们看 从 return 开始的每一个语句代表者什么
2.1 saveModule(CONTROLLER_KEY, target);
首先我们分析下 第一个执行的语句
export const CONTROLLER_KEY = 'web:controller';// saveModule(CONTROLLER_KEY, target);/*** save module to inner map* @param decoratorNameKey 装饰器名称* @param target class 对象*/export function saveModule(decoratorNameKey: ObjectIdentifier, target) {// 判断是否是 类if (isClass(target)) {// 在改对象上存储一个 装饰器saveProviderId(undefined, target);}return manager.saveModule(decoratorNameKey, target);}
2.1.1 saveProviderId
我们看下 saveProviderId 的 执行逻辑
// saveexport const TAGGED_CLS = 'injection:tagged_class';const uuid = generateRandomId();// save class id and uuidsaveClassMetadata(TAGGED_CLS,{id: identifier,originName: target.name,uuid,name: classNamed(target.name),},target);
这里是 逻辑 看下 saveClassMetadata 的ts定义可知
export function saveClassMetadata(decoratorNameKey: ObjectIdentifier,data: any,target: any,mergeIfExist?: boolean) {}
相当于在 我们之前讲过 的 defineMetadata 之后 我会单独出一篇 关于 内部的 metadata 操作的文章
2.1.2 manager.saveModule
这个方法就很简单了 我们在看到依赖注入的时候 所有的 service 都是注入到容器里面的 这里的 manager 就是 上面代🈯️的容器 而 saveModule 就是 manager 提供的方法
代码如下
export class DecoratorManager extends Map implements IModuleStore {// 模块注入到容器中// 这里的 key 就是 web:controller export const CONTROLLER_KEY = 'web:controller';// 这里的模块就是我们的 HomeControllersaveModule(key, module) {// 这里判读判断是否有用户自定义容器传进来if (this.container) {return this.container.saveModule(key, module);}// 内置容器逻辑if (!this.has(key)) {this.set(key, new Set());}this.get(key).add(module);}}
当我们 不会手动传 container 容器进来的时候 一般都是走内置的逻辑
这里我们看到 所有的 controller 都是 存在 key 为 web:controller 的map 里面 值是 set的格式
2.2 saveClassMetaData
第二部分就是 关于 prefix的判断执行的逻辑
一般默认情况下 prefix 为true 都会 成立
继续看下面的代码
if (prefix)saveClassMetadata(CONTROLLER_KEY,{prefix,routerOptions,} as ControllerOption,target);
这段也很简单 就是在 HomeController 上面 新增了一个装饰器 key 就是 CONTROLLER_KEY
截止到上面的代码 我们已经在 HomeController 新增了两个装饰器 一个是
saveClassMetadata(TAGGED_CLS,{id: identifier,originName: target.name,uuid,name: classNamed(target.name),},target);
另一个就是上面的
saveClassMetadata(CONTROLLER_KEY,{prefix,routerOptions,} as ControllerOption,target);
2.3 Scope
第三个执行的语句则是
Scope(ScopeEnum.Request)(target);
这个是作用域的装饰器
先看 ScopeEnum的定义
export enum ScopeEnum {Singleton = 'Singleton',Request = 'Request',Prototype = 'Prototype',}
这里说明了 Scope作用域的三种类型
具体关于作用域的文档 可以详见 依赖注入-作用域
这里关于 Scope的 代码如下
export function Scope(scope: ScopeEnum): ClassDecorator {return function (target: any): void {saveObjectDefinition(target, { scope });};}
最新版本的scope 应该取消了 单例的默认 (我提的~~)
这里最终执行 saveObjectDefinition 其实也是装饰器的新增
export const OBJ_DEF_CLS = 'injection:object_definition_class';/*** save class object definition* @param target class* @param props property data*/export function saveObjectDefinition(target: any, props = {}) {saveClassMetadata(OBJ_DEF_CLS, props, target, true);return target;}
所以 到这里 我们已经在 HomeController 新增了三个装饰器
2.4 Provide
最后的一步是 Provide()(target)
关于 Provide 装饰器的源码 如下
export function Provide(identifier?: ObjectIdentifier) {return function (target: any) {// 也就是 @Provide 装饰器最终执行的就行// 一般来说 Provide 是放在定义service 的外层 也就是说一般用作类装饰器// 那这里的 target 的值 一般就是 class本身return saveProviderId(identifier, target);};}
这个是想要干啥呢 ???? 感觉逻辑有点重复
