1. 前言
我们可以在 midway控制器 篇中可以看到 路由方法 提供的路由方法装饰器 代码如下
// src/controller/home.ts
import { 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 的 执行逻辑
// save
export const TAGGED_CLS = 'injection:tagged_class';
const uuid = generateRandomId();
// save class id and uuid
saveClassMetadata(
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';
// 这里的模块就是我们的 HomeController
saveModule(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);
};
}
这个是想要干啥呢 ???? 感觉逻辑有点重复