1. 前言

我们可以在 midway控制器 篇中可以看到 路由方法 提供的路由方法装饰器 代码如下

  1. // src/controller/home.ts
  2. import { Controller, Get, Provide } from '@midwayjs/decorator';
  3. @Provide()
  4. @Controller('/')
  5. export class HomeController {
  6. @Get('/')
  7. async home() {
  8. return 'Hello Midwayjs!';
  9. }
  10. @Post('/update')
  11. async updateData() {
  12. return 'This is a post method'
  13. }
  14. }

Midway 还提供了其他的装饰器, @Get@Post@Put()@Del()@Patch()@Options()@Head()@All() ,表示各自的 HTTP 请求方法。

以上全部摘抄自官方文档 然后按照 从 @midwayjs/decorator 从 来分析 Controller 以及上面描述的这些HTTP方法

2. Controller

首先 我们看到的 整个 controller的控制器
在 midway 中源码如下

  1. export function Controller(
  2. prefix = '/',
  3. routerOptions: {
  4. sensitive?: boolean;
  5. middleware?: MiddlewareParamArray;
  6. description?: string;
  7. tagName?: string;
  8. } = { middleware: [], sensitive: true }
  9. ): ClassDecorator {
  10. return (target: any) => {
  11. //
  12. saveModule(CONTROLLER_KEY, target);
  13. if (prefix)
  14. saveClassMetadata(
  15. CONTROLLER_KEY,
  16. {
  17. prefix,
  18. routerOptions,
  19. } as ControllerOption,
  20. target
  21. );
  22. Scope(ScopeEnum.Request)(target);
  23. Provide()(target);
  24. };
  25. }

首先 我们可以想到的是 controller 在使用上 是一个类装饰器
它的入参 是 prefix 和 routerOptions

一般我们在使用的时候 最多就是 设置一个 prefix

这些都很容易理解

接着我们看 从 return 开始的每一个语句代表者什么

2.1 saveModule(CONTROLLER_KEY, target);

首先我们分析下 第一个执行的语句

  1. export const CONTROLLER_KEY = 'web:controller';
  2. // saveModule(CONTROLLER_KEY, target);
  3. /**
  4. * save module to inner map
  5. * @param decoratorNameKey 装饰器名称
  6. * @param target class 对象
  7. */
  8. export function saveModule(decoratorNameKey: ObjectIdentifier, target) {
  9. // 判断是否是 类
  10. if (isClass(target)) {
  11. // 在改对象上存储一个 装饰器
  12. saveProviderId(undefined, target);
  13. }
  14. return manager.saveModule(decoratorNameKey, target);
  15. }

2.1.1 saveProviderId

我们看下 saveProviderId 的 执行逻辑

  1. // save
  2. export const TAGGED_CLS = 'injection:tagged_class';
  3. const uuid = generateRandomId();
  4. // save class id and uuid
  5. saveClassMetadata(
  6. TAGGED_CLS,
  7. {
  8. id: identifier,
  9. originName: target.name,
  10. uuid,
  11. name: classNamed(target.name),
  12. },
  13. target
  14. );

这里是 逻辑 看下 saveClassMetadata 的ts定义可知

  1. export function saveClassMetadata(
  2. decoratorNameKey: ObjectIdentifier,
  3. data: any,
  4. target: any,
  5. mergeIfExist?: boolean
  6. ) {}

相当于在 我们之前讲过 的 defineMetadata 之后 我会单独出一篇 关于 内部的 metadata 操作的文章

2.1.2 manager.saveModule

这个方法就很简单了 我们在看到依赖注入的时候 所有的 service 都是注入到容器里面的 这里的 manager 就是 上面代🈯️的容器 而 saveModule 就是 manager 提供的方法

代码如下

  1. export class DecoratorManager extends Map implements IModuleStore {
  2. // 模块注入到容器中
  3. // 这里的 key 就是 web:controller export const CONTROLLER_KEY = 'web:controller';
  4. // 这里的模块就是我们的 HomeController
  5. saveModule(key, module) {
  6. // 这里判读判断是否有用户自定义容器传进来
  7. if (this.container) {
  8. return this.container.saveModule(key, module);
  9. }
  10. // 内置容器逻辑
  11. if (!this.has(key)) {
  12. this.set(key, new Set());
  13. }
  14. this.get(key).add(module);
  15. }
  16. }

当我们 不会手动传 container 容器进来的时候 一般都是走内置的逻辑

这里我们看到 所有的 controller 都是 存在 key 为 web:controller 的map 里面 值是 set的格式

2.2 saveClassMetaData

第二部分就是 关于 prefix的判断执行的逻辑
一般默认情况下 prefix 为true 都会 成立
继续看下面的代码

  1. if (prefix)
  2. saveClassMetadata(
  3. CONTROLLER_KEY,
  4. {
  5. prefix,
  6. routerOptions,
  7. } as ControllerOption,
  8. target
  9. );

这段也很简单 就是在 HomeController 上面 新增了一个装饰器 key 就是 CONTROLLER_KEY

截止到上面的代码 我们已经在 HomeController 新增了两个装饰器 一个是

  1. saveClassMetadata(
  2. TAGGED_CLS,
  3. {
  4. id: identifier,
  5. originName: target.name,
  6. uuid,
  7. name: classNamed(target.name),
  8. },
  9. target
  10. );

另一个就是上面的

  1. saveClassMetadata(
  2. CONTROLLER_KEY,
  3. {
  4. prefix,
  5. routerOptions,
  6. } as ControllerOption,
  7. target
  8. );

2.3 Scope

第三个执行的语句则是

  1. Scope(ScopeEnum.Request)(target);

这个是作用域的装饰器
先看 ScopeEnum的定义

  1. export enum ScopeEnum {
  2. Singleton = 'Singleton',
  3. Request = 'Request',
  4. Prototype = 'Prototype',
  5. }

这里说明了 Scope作用域的三种类型
具体关于作用域的文档 可以详见 依赖注入-作用域

这里关于 Scope的 代码如下

  1. export function Scope(scope: ScopeEnum): ClassDecorator {
  2. return function (target: any): void {
  3. saveObjectDefinition(target, { scope });
  4. };
  5. }

最新版本的scope 应该取消了 单例的默认 (我提的~~)
这里最终执行 saveObjectDefinition 其实也是装饰器的新增

  1. export const OBJ_DEF_CLS = 'injection:object_definition_class';
  2. /**
  3. * save class object definition
  4. * @param target class
  5. * @param props property data
  6. */
  7. export function saveObjectDefinition(target: any, props = {}) {
  8. saveClassMetadata(OBJ_DEF_CLS, props, target, true);
  9. return target;
  10. }

所以 到这里 我们已经在 HomeController 新增了三个装饰器

2.4 Provide

最后的一步是 Provide()(target)

关于 Provide 装饰器的源码 如下

  1. export function Provide(identifier?: ObjectIdentifier) {
  2. return function (target: any) {
  3. // 也就是 @Provide 装饰器最终执行的就行
  4. // 一般来说 Provide 是放在定义service 的外层 也就是说一般用作类装饰器
  5. // 那这里的 target 的值 一般就是 class本身
  6. return saveProviderId(identifier, target);
  7. };
  8. }

这个是想要干啥呢 ???? 感觉逻辑有点重复