第五章:从装饰器应用到底层JS深入+装饰器实战(下)

177 阅读25分钟

慕课网 TS 高级课程

第五章:从装饰器应用到底层JS深入+装饰器实战

技能大纲

10-1 本章概述,熟练透彻掌握装饰器对职业发展有何意义?
10-2 【装饰器概述】装饰器定义,分类,要解决的问题
10-3 【类装饰器】类装饰器的两种实现
10-4 【类装饰器底层源码】逐行深剖底层 JS 源码
10-5 【泛型工厂类继承装饰器】 泛型工厂类继承装饰器实现和意义
10-6 【泛型工厂类继承装饰器底层源码】逐行深剖+优化底层 JS 源码

10-7 【泛型工厂类匿名类+继承装饰器】匿名类在装饰器中的实现

10-8 【方法装饰器】 拦截器意义,如何实现前置丶后置拦截器功能?【真实应用场景】
10-9 【方法装饰器底层源码】逐行深剖底层 JS 源码

10-10 【方法装饰器底层源码 】方法拦截功能功能为什么会失效?

10-11 【属性装饰器】属性装饰器应用——顾客等级说明实现 1

10-11 【属性装饰器底层源码】 逐行解析底层JS源码 2

10-12 【属性丶类丶方法装饰器综合应用】依赖注入+请求方法,控制器初步实现

10-13 【参数装饰器】参数装饰器实现+ 详解底层JS源码
10-14 【构造器参数装饰器】构造器参数装饰器依赖注入实现
10-15 【多个装饰器组合执行】 类丶属性丶方法 丶参数装饰器组合+执行顺序

10-16 【元数据操作】 理解 reflect-metadata 元数据操作重载方法和其他方法

10-17 [元数据操作] 3个重要且特殊的内置元数据

10-18 【仿 Nestjs 装饰器实战准备】经典案例透彻理解依赖注入(DI)的好处

10-19 【仿 Nestjs 装饰器实战准备】 项目分层+ 依赖注入实现准备
10-20 【仿 Nestjs 装饰器实战】 依赖注入实现准备——优化存储

10-21 【仿 Nestjs 装饰器实战】 依赖注入实现和升级自动装配装饰器

10-22 【仿 Nestjs 装饰器实战】 依赖注入引发的深度思考

10-23 【仿 Nestjs 装饰器实战】 依赖注入实现引发的深度思考+代码持续升级优化

10-24 【仿 Nestjs 装饰器实战】数据访问层和实体层封装

10-25 【仿 Nestjs 装饰器实战】控制器装饰器+路由器功能实现

10-26 【仿 Nestjs装饰器实战】多种请求方法装饰器实现
10-27 【仿 Nestjs装饰器实战】多页面请求+中间件装饰器实现
10-28 【仿 Nestjs装饰器实战】多个中间件装饰器如何执行?
10-29 【仿 Nestjs装饰器实战】解析路由器底层复杂泛型

10-30 【仿 Nestjs装饰器实战+深度思考题+作业】 优化依赖注入对象调用

慕课网 TS 高级课程

10-13-1 【参数装饰器】参数装饰器实现

function UrlParam(params: any) {
  return function paramDecorator(targetClassPrototype: any,
    methodname: string, paramindex: number) {
    console.log("targetClassPrototype:", targetClassPrototype)
    console.log("methodname:", methodname);
    console.log("paramindex:", paramindex);
    targetClassPrototype.info = params
  }
}
class People {
  eat(@UrlParam("地址信息") address: string, who: string) {
    console.log("address:", address);
  }
}

慕课网 TS 高级课程

10-13-2【参数装饰器底层源码】 逐行解析底层 JS 源码

"use strict";
// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // argsnum 参数个数
  var argsnum = arguments.length;
  // targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
  // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
  // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
  // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
  var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
  // decorator保存装饰器数组元素
  var decorator;
  // 元数据信息,支持reflect-metadata元数据
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
    targetinfo = Reflect.decorate(decorators, target, key, desc);
  } else
    //  装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
    for (var i = decorators.length - 1; i >= 0; i--) {
      if (decorator = decorators[i]) {
        // 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
        // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
        // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
        // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
        targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
          decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
        console.log("targetinforesult:", targetinfo)
      }
    }
  return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层JS 组合装饰器和目标类 __decorate函数结束


// Object.defineProperty(exports, "__esModule", { value: true });
// 2 带参数的装饰器
function UrlParam(params) {
    return function paramDecorator(targetClassPrototype, methodname, paramindex) {
        console.log("targetClassPrototype:", targetClassPrototype);
        console.log("methodname:", methodname);
        console.log("paramindex:", paramindex);
        targetClassPrototype.info = params;
    };
}

// 3 封装带参数装饰器
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var People = /** @class */ (function () {
    function People() {
    }
    People.prototype.eat = function (address, who) {
        console.log("address:", address);
    };
    __decorate([
        __param(0, UrlParam("地址信息"))
    ], People.prototype, "eat", null);
    return People;
}());

慕课网 TS 高级课程

10-14 【构造器参数装饰器】构造器参数装饰器注入实现

课程安排

  1. 先安装 reflect-metadata 第三方包 【后面会有详细讲解,先简单了解即可】

  2. UserController 控制器类实现

  3. UserService 业务类实现

  4. 保存对象的集合类实现 【单件设计模式】

  5. 构造参数装饰器实现

// 1 先安装 cnpm i reflect-metadata -D

// 2 UserController.ts
import { InjectContructor } from './InjectContructorDecorator'
import UserService from './UserService'
import collectionInstance from './Collection'
class UserController {

   constructor(@InjectContructor("userService")
         private userService: UserService, private count: string) { 

  }

  public login() {
    let peopleServiceInstace = collectionInstance.get("userService");
    peopleServiceInstace.login();
  }
}
let controller = new UserController();
controller.login();

// 3 构造参数装饰器
import 'reflect-metadata'
import collectionInstance from './Collection'
type MyParameterDecorator = (target: Object,
  paramname: string | symbol, parameterIndex: number) => void;
export function InjectContructor(injectid?: string): MyParameterDecorator {
  return (target, paramname, index) => {
     // 获取 target 目标类上构造函数所有参数类型
    const InjectConstructorClass = Reflect.getMetadata("design:paramtypes", target);
    console.log("InjectConstructorClass:", InjectConstructorClass)
    let  InjectConstructorClassobj = new InjectConstructorClass[index]()
    collectionInstance.set(injectid, InjectConstructorClassobj);
  }
}

// 4 UserService业务类
export class UserService {
  pname: string = "人民"
  public login() {
    console.log(this.pname + "登录....");
  }
}

// 5 保存对象的集合类【单件设计模式】
export class Collection<T = any> {
  static collection: Collection = new Collection();
 
  private constructor() {
    console.log("构造。。。。");
  }

  public static test() {
    console.log("test....");
  }
  private containerMap = new Map<string | symbol, any>();

  public set(id: string | symbol, value: T): void {
    this.containerMap.set(id, value);
  }

  public get(id: string | symbol): T {
    return this.containerMap.get(id);
  }

  public has(id: string | symbol): Boolean {
    return this.containerMap.has(id);
  }
}
export default Collection.collection;




export { }

慕课网 TS 高级课程

10-15 【多个装饰器组合执行】 类、属性、方法 、参数装饰器组合+执行顺序

课程安排:

  1. 类、属性、方法 、参数装饰器组合案例实现
  2. 执行顺序
  3. 底层源码
// 1.  类丶属性丶方法 丶参数装饰器组合案例实现   2.  执行顺序 
function firstMethodDecorator(targetClassPrototype: any,
  methodname: string) {
  console.log("=============执行第一个方法装饰器==============")
  console.log("类名:", targetClassPrototype)//  类原型对象变量   URLInfo { show: [Function] }
  console.log("方法名:", methodname);//key
}


function secondMethodDecorator(params: string) {
  return function (targetClassPrototype: any, methodname: string) {
    console.log("=============执行第二个方法装饰器==============")
    console.log("类名:", targetClassPrototype)//  类原型对象变量   URLInfo { show: [Function] }
    console.log("方法名:", methodname);//key
  }
}

function paramDecorator(targetClassPrototype: any, paramname: string, paramindex: number) {
  console.log("=============执行参数装饰器==============")
  console.log("targetClassPrototype:", targetClassPrototype);
  console.log("参数名:", paramname);
  console.log("参数索引:", paramindex);
}


function UrlPropDecorator(targetClassPrototype: any, attrname: any) {

  console.log("=============执行属性装饰器==============")
  console.log("targetClassPrototype:", targetClassPrototype);
  console.log("属性名:", attrname);
}


function URLInfoDecorator(targetClassPrototype: any) {
  console.log("==========类装饰器============")
  console.log("targetClassPrototype:", targetClassPrototype);
}

function constructorDecorator(params: any) {
  return function (targetClassPrototype: any, paramname: string, paramindex: number) {
    console.log("==========构造器参数装饰器============")
    console.log("构造器参数装饰器", targetClassPrototype);
    console.log("构造器参数名为:", paramname);
    console.log("构造器参数索引位置:", paramindex);
  }
}

@URLInfoDecorator
class URLInfo {
  constructor(@constructorDecorator("url") public uri: string) {

  }

  @UrlPropDecorator
  public url: string = "https://www.imooc.com"


  @firstMethodDecorator
  methodOne(@paramDecorator data: string) {
    console.log("this:", this);
    console.log("目标类:", this.uri)
  }

  @secondMethodDecorator("yes")
  methodTwo(@paramDecorator address: string) {
    console.log(address)
  }
}
export { }

3. 底层源码

// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // argsnum 参数个数
  var argsnum = arguments.length;
  // targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
  // argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
  // argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
  // argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
  var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
  // decorator保存装饰器数组元素
  var decorator;
  // 元数据信息,支持reflect-metadata元数据
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
    targetinfo = Reflect.decorate(decorators, target, key, desc);
  } else
    //  装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
    for (var i = decorators.length - 1; i >= 0; i--) {
      if (decorator = decorators[i]) {
        // 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
        // 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo) 
        // 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
        // targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
        targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
          decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
        console.log("targetinforesult:", targetinfo)
      }
    }
  return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层JS 组合装饰器和目标类 __decorate函数结束

function firstMethodDecorator(targetClassPrototype, methodname) {
    console.log("=============执行第一个方法装饰器==============");
    console.log("类名:", targetClassPrototype); //  类原型对象变量   URLInfo { show: [Function] }
    console.log("方法名:", methodname); //key
}
function secondMethodDecorator(params) {
    return function (targetClassPrototype, methodname) {
        console.log("=============执行第二个方法装饰器==============");
        console.log("类名:", targetClassPrototype); //  类原型对象变量   URLInfo { show: [Function] }
        console.log("方法名:", methodname); //key
    };
}
function paramDecorator(targetClassPrototype, paramname, paramindex) {
    console.log("=============执行参数装饰器==============");
    console.log("targetClassPrototype:", targetClassPrototype);
    console.log("参数名:", paramname);
    console.log("参数索引:", paramindex);
}
function UrlPropDecorator(targetClassPrototype, attrname) {
    console.log("=============执行属性装饰器==============");
    console.log("targetClassPrototype:", targetClassPrototype);
    console.log("属性名:", attrname);
}
function URLInfoDecorator(targetClassPrototype) {
    console.log("==========类装饰器============");
    console.log("targetClassPrototype:", targetClassPrototype);
}
function constructorDecorator(params) {
    return function (targetClassPrototype, paramname, paramindex) {
        console.log("==========构造器参数装饰器============");
        console.log("构造器参数装饰器", targetClassPrototype);
        console.log("构造器参数名为:", paramname);
        console.log("构造器参数索引位置:", paramindex);
    };
}
var URLInfo = /** @class */ (function () {
    function URLInfo(uri) {
        this.uri = uri;
        this.url = "https://www.imooc.com";
    }
    URLInfo.prototype.methodOne = function (data) {
        console.log("this:", this);
        console.log("目标类:", this.uri);
    };
    URLInfo.prototype.methodTwo = function (address) {
        console.log(address);
    };
    __decorate([
        UrlPropDecorator
    ], URLInfo.prototype, "url", void 0);
    __decorate([
        firstMethodDecorator,
        __param(0, paramDecorator)
    ], URLInfo.prototype, "methodOne", null);
    __decorate([
        secondMethodDecorator("yes"),
        __param(0, paramDecorator)
    ], URLInfo.prototype, "methodTwo", null);
    URLInfo = __decorate([
        URLInfoDecorator,
        __param(0, constructorDecorator("url"))
    ], URLInfo);
    return URLInfo;
}());

慕课网 TS 高级课程

10-16 【元数据操作】 理解 reflect-metadata 元数据操作重载方法和其他方法

课程安排

  1. 元数据定义、作用
  2. 对象和对象属性上使用元数据
  3. 直接在类,方法上定义元数据
  4. 直接在类属性上定义元数据
  5. 在装饰器内部为类,方法,属性定义或者获取元数据
  6. metakey 相同,是否会覆盖?

1. 元数据定义、作用

1.1 什么是元数据

元数据指附加在对象、类、方法、属性丶参数上的数据。

1.2 元数据作用

元数据用来帮助提供实现某种业务功能需要用到的数据。

2. 对象和对象属性上使用元数据

步骤: 1. 安装 reflect-metadata 第三方库包

 cnpm i reflect-metadata -S

步骤2:代码:

import 'reflect-metadata'
// 1. 对象
let obj = {
  username: "罗斯福",
  age: 23,
  info() {
    console.log("信息");
  }
}
// 2. 使用 Reflect.defineMetadata 定义元数据。

// 2.1  Reflect.defineMetadata 是一个重载的方法
//  定义格式 
//  为类或者对象上定义元数据
 Reflect.defineMetadata(metakey,metavalue,targetClassOrObject)
//  为方法定义元数据
 Reflect.defineMetadata(metakey,metavalue,targetprototype,methodname)
//  为属性定义元数据
 Reflect.defineMetadata(metakey,metavalue,targetprototype,propkey)

// 说明:打开 d.ts 定义描述文件说明:Reflect 是命名空间,defineMetadata 是命名空间中的一个方法。

//  2.2 在对象上定义元数据
Reflect.defineMetadata('firstdescribe', '对象属性全部符合要求', obj);
Reflect.defineMetadata('seconddescribe', '对象不可删除', obj);

//  2.3 获取obj上metakey为 firstdescribe 的值
console.log(Reflect.getMetadata('firstdescribe', obj))// 输出对象属性全部符合要求

//  2.4 获取obj上metakey不存在的值
console.log(Reflect.getMetadata('threedescribe', obj))// 输出undefined

//  3  使用 Reflect.defineMetadata 在对象属性上定义元数据。
//  3.1 在对象属性上定义和获取元数据
Reflect.defineMetadata('usernamemetakey', '用户名合法', obj,"username");
Reflect.getMetadata('usernamemetakey', obj, "username"));// 输出用户名合法

//  3.2 使用 Reflect.hasMetadata 查看对象或对象属性上是否存在某个元数据
if (Reflect.hasMetadata('describe', obj)) {
  console.log("obj存在describe元数据");
}

3 直接在类,方法上定义元数据


// 1. 在类上定义元数据
@Reflect.metadata('decribe', '都是地球人')
class People {
  @Reflect.metadata("descible", "姓名不能包含非法汉字")
  username = "wangwu"
  @Reflect.metadata("importinfo", "去吃陶然居好吗")
  eat() {

  }
}
// 2
// 2.1 获取类上的元数据
console.log(Reflect.getMetadata('decribe', People));// 都是地球人

// 2.2 获取方法上的元数据 第二个参数是原型
console.log(Reflect.getMetadata('importinfo', People.prototype, 'eat'));//去吃陶然居好吗

// 2.3 判断People.prototype 原型上 eat 方法上是否存在importinfo元数据
if (Reflect.hasMetadata('importinfo', People.prototype, 'eat')) {
  console.log("hasMetadata=>People原型上存在eat方法的importinfo元数据");
}


// 3 定义子类
class ChinesePeople extends People {
  guoYear() {

  }
}

//  4 子类获取父类原型上的方法 ———— hasMetadata
if (Reflect.hasMetadata('importinfo', ChinesePeople.prototype, 'eat')) {
  console.log("hasMetadata=>ChinesePeople原型上通过继承也获取到了eat方法和eat方法的importinfo元数据");
}

//  5  获取自有元数据,但不能获取原型链上父类的元数据 ———— hasOwnMetadata
if (Reflect.hasOwnMetadata('importinfo', ChinesePeople.prototype, 'eat')) {
  console.log("hasOwnMetadata=>ChinesePeople原型上存在eat方法的importinfo元数据");
} else {
  console.log("hasOwnMetadata=>ChinesePeople原型上不存在eat方法的importinfo元数据");
}

4 直接在类属性上定义元数据

import 'reflect-metadata'

// 为类定义元数据
@Reflect.metadata("info", "地球人")
class People {
  @Reflect.metadata('descible1', '居住地为主要城市')
  @Reflect.metadata('descible2', '上海')
  place: Array<string> = ["中国", "北京"]

  @Reflect.metadata('firstname', '第一个名字')
  @Reflect.metadata('lastname', '最后一个名字')
  getFullName(name: string, age: string): number {
    return 100
  }
}

// 获取元数据
console.log(Reflect.getMetadata('info', People));//地球人
console.log(Reflect.getMetadata("descible", People.prototype, 'place'));//rose
console.log(Reflect.getMetadata('firstname', People.prototype, 'getFullName'))//Jim
console.log(Reflect.getMetadata('lastname', People.prototype, 'getFullName'))//Jim
// [
//   'design:returntype',
//   'design:paramtypes',
//   'design:type',
//   'lastname',
//   'firstname'
// ]
// // 获取People.prototype 上getFullName方法的全部元数据Key组成的数组
//console.log(Reflect.getMetadataKeys(People.prototype, "getFullName"));
// Reflect.getMetadataKeys(People.prototype).forEach((item) => {
//   console.log("metadatakey:", item);
// })
// Reflect.getMetadataKeys(People.prototype, 'getFullName').forEach((metakey) => {
//   console.log("11metadatakey:", metakey);
//   console.log(Reflect.getMetadata(metakey, People.prototype, 'getFullName'));
// })

// 获取People类上place方法的全部元数据Key组成的数组
// 输出
// [
//   'design:type',
//   'descible1',
//   'descible2'
// ]
console.log(Reflect.getMetadataKeys(People.prototype, "place"));

Reflect.getMetadataKeys(People.prototype, 'place').forEach((metakey) => {
  console.log("属性metadatakey:", metakey);
  console.log(Reflect.getMetadata(metakey, People.prototype, 'place'));
})


class ChinesePeople extends People {
  @Reflect.metadata("descible", "姓名不能包含非法汉字")
  guoYear(args: string) {

  }
}
console.log("getMetadataKeys==>查看父类上的方法...");
console.log(Reflect.getMetadataKeys(ChinesePeople.prototype, 'getFullName'))//Jim
console.log("getOwnMetadataKeys不能查看父类上的方法...");
console.log(Reflect.getOwnMetadataKeys(ChinesePeople.prototype, 'getFullName'))//Jim

5 在装饰器内部为类,方法,属性定义或者获取元数据

参见10-12 ——方法装饰器中定义的元数据之后类装饰器获取方法上定义过的元数据

6 metakey 相同,是否会覆盖?

import 'reflect-metadata'

// 直接为类或属性或方法定义元数据

// 为类定义元数据
@Reflect.metadata('decribe', '都是地球人')
class People {
  username = "wangwu"

  eat() {

  }
}

@Reflect.metadata('decribe', '木星和太阳')
class Customer {
  username = "wangwu"
  eat() {

  }
}


// 获取元数据
console.log(Reflect.getMetadata('decribe', People));// 都是地球人

慕课网 TS 高级课程

10-17 [元数据] 3 个重要且特殊的内置元数据 key

课程安排:主要理解以下三个类型元数据

**条件:**在当前装饰器修饰前提下执行下面元数据 key

  1. design:paramtypes a. 构造器所有参数数据类型组成的数组 b. 类中方法全部参数的数据类型组成的数组

  2. design:type a. 获取类属性的数据类型 b. 获取类方法参数的数据类型

  3. design:returntype a. 获取类方法返回值的数据类型

// 1. 构造器装饰器
import 'reflect-metadata'
import ContainerInstance from './container'
type MyParameterDecorator = (target: Object,
  paramname: string | symbol, parameterIndex: number) => void;

export function InjectContructor(injectid?: string): MyParameterDecorator {
  return (target, paramname, index) => {

    console.log(" 进入构造函数的参数装饰器....", target);
    console.log("获取类构造器参数类型组成的数组....")
    const constructorParamTypeArr = Reflect.getMetadata("design:paramtypes", target);
    console.log("design:paramtypes:", constructorParamTypeArr)

    let injectInstance = new constructorParamTypeArr[index]()
    ContainerInstance.set(injectid, injectInstance);
  }
}

// 2. 方法装饰器
type MethodDecorator = <T>(target: Object, propertyKey: string | symbol,
  descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;

export function get(injectid?: string): MethodDecorator {
  return (targetprototype, methodname, descriptor) => {
    console.log(" 进入方法装饰器....", targetprototype);

    const methodParamTypeArr = Reflect.getMetadata("design:paramtypes", targetprototype, methodname);
    console.log("design:paramtypes:", methodParamTypeArr)

    const methodReturnTypeArr = Reflect.getMetadata("design:returntype", targetprototype, methodname);
    console.log("design:returntype:", methodReturnTypeArr)
  }
}

// 3. 属性装饰器
type MyPropertyDecorator = (target: any, propertyKey: string | symbol) => void;
import collection from './Collection'
export function InjectProperty(inject?: any): MyPropertyDecorator {

  return function (targetproperty, propertyKey) {

    console.log(" 进入属性参数装饰器....", targetproperty);

    console.log("获取类属性的数据类型");
    const propType = Reflect.getMetadata("design:type", targetproperty, propertyKey);
    console.log("design:type:", propType)
  }
}

// 4. 集合类
class Collection<T = any> {
  static collection: Collection = new Collection();
  private constructor() { }

  private containerMap = new Map<string | symbol, any>();

  public set(id: string | symbol, value: T): void {
    this.containerMap.set(id, value);
  }

  public get(id: string | symbol): T {
    return this.containerMap.get(id);
  }

  public has(id: string | symbol): Boolean {
    return this.containerMap.has(id);
  }
}
export default Collection.collection;

// 5 控制器类
import { UserService } from './UserService'
import collection from './Collection'
import InjectContructor from './InjectContructorDecorator'
import { get } from './MyMethodDecorator'
import { InjectProperty } from './MyPropertyDecorator'
class UserController {

  @InjectProperty("userService")
  private userService!: UserService
  constructor(@InjectContructor("userService") private peopleService?: UserService, count?: number) {

  }

  @get("/path")
  public login(username: string, pwd: number): number {
    let peopleServiceInstace = collection.get("userService");
    peopleServiceInstace.login();
    console.log("进入login方法...")
    return 3
  }
}
let controller = new UserController();
controller.login("wangwu", 23);
export { }

// 6 业务逻辑层类
export class UserService {
  pname: string = "人民"
  public login() {
    console.log(this.pname + "登录....");
  }
}

export { }

10-18 【仿 Nestjs 装饰器实战准备】多门语言都有的依赖注入有什么好处?

大大降低类和关联类的耦合度,课堂举例打印机模式讲解依赖注入的好处。

10-19 【仿 Nestjs 装饰器实战准备】 项目分层

环境安装: yarn add express express-session reflect-metadata -S

分为

  1. service 业务逻辑层

  2. controller 层

  3. 装饰器 decorator 层

  4. 中间件 middleaware 层

  5. 路由 router 层

  6. collection集合层 【也是容器层】

  7. util 层

  8. 启动文件

慕课网 TS 高级课程

10-20 【仿 Nestjs 装饰器实战】 依赖注入实现准备

课程安排:1. 对10-12 代码 增加,修改

1 UserService 类


export default class UserService {
  Login(username: string, pwd: string, role: string) {
    console.log("进入service ...Login,username:", username)

    if (username === "admin" && pwd === "123" && role === "admin") {
      return true;
    } else {
      return false;
    }
  }
  register() {
    console.log("usersevice...register")
  }
}

2 控制器——UserController

import Autowired from '../decorator/autowireddecortator'
import UserService from '../service/UserService'
import CollectionInstance from '../collection/'
// 装饰器执行顺序: 1.属性装饰器==>2.方法参数装饰器==>3.方法装饰器===>4.类装饰器
class UserController {

  @Autowired("userService")//  修改Inject 为更专业的 Autowired 单词
  private userService!: UserService // 修改Inject 为更专业的 Autowired 单词


  public login(): void {
    // 增加....
  }
}
let controller = new UserController();
controller.login();
export { }

3 为控制器属性注入对象的注入装饰器——autowireddecortator.ts

import 'reflect-metadata'
import collectionInstance from '../collection'

type MyPropDecorator = (targetClassPrototype: any, propertyKey: string | symbol) => void
export default function Autowired(injectid: string): MyPropDecorator {
  return (targetClassPrototype, propertyKey) => {
    // PropClass=UserService类
    let PropClass = Reflect.getMetadata("design:type", targetClassPrototype, propertyKey)
    //  增加....
  }
}

慕课网 TS 高级课程

10-20 【仿 Nestjs 装饰器实战】 依赖注入实现准备——优化存储

import 'reflect-metadata'
import collectionInstance from '../collection'

type MyPropDecorator = (targetClassPrototype: any, propertyKey: string | symbol) => void
export default function Autowired(injectid: string): MyPropDecorator {
  return (targetClassPrototype, propertyKey) => {
    // PropClass=UserService类
    let PropClass = Reflect.getMetadata("design:type",
      targetClassPrototype, propertyKey)
    //  增加....
    let PropClassObj = new PropClass();
    //collectionInstance.set(propertyKey, PropClassObj);
    // 对比:Object.defineProperty
    // 好处:由于targetClassPrototype原型+propertyKey一起是绝对不会被覆盖的
    // 充分保证了数据属性中的value的对象的唯一性
    Reflect.defineProperty(targetClassPrototype, propertyKey,
      { value: PropClassObj })
  }
}

10-21 【仿 Nestjs 装饰器实战】 依赖注入实现和升级自动装配装饰器

// 10-21 【仿 Nestjs 装饰器实战】 依赖注入实现和升级自动装配装饰器
//  实现步骤   1. 建立伪接口类 UserServiceInter
//            2. 修改UserService的名字为userServiceImpl类
//            3. 修改自动装配装饰器【Autowired】代码:见增加和修改部分
//          最后别忘了修改UserController中的login方法中的S100中的属性名为userServiceImpl
10-22 【仿 Nestjs 装饰器实战】 依赖注入实现引发的深度思考

课程安排

  1. 理解实战涉及到的主要专业术语
  2. 依赖注入中的单件模式实现方式1

1 理解实战涉及到的主要专业术语 【比较抽象,先初步理解,随代码讲解逐步加深理解】

@Controller 控制器装饰器【@Controller】 修饰的类,是用来支持页面的各种请求的类。 @Service 业务逻辑层类装饰器

@Autowired 自动装配,一般是帮助把外部其他数据注入【简单理解为赋值】给当前类属性或方法参数的装饰器,这些数据可以是string,number等基本数据类型,也可以是一个对象。 dependencyid [injectid] 依赖ID,一个唯一标识符变量, 作为@Autowired装饰器函数的实参,使用@Autowired 为 不同类属性,或方法参数注入数据时,dependencyid 用于区分这些不同的类。 singleton 标注是否是单例注入的参数,可选。

2 依赖注入中的单件模式实现方式1

import 'reflect-metadata'
import collectionInstance from '../collection'

type MyPropDecorator = (targetClassPrototype: any, propertyKey: string | symbol) => void
/**
 * 
 * @param dependencyid[injectid] 依赖id
 * @param singleton -判断是否是单件模式的对象 
 * @returns 
 */
export default function Autowired(dependencyid?: string, singleton?: boolean): MyPropDecorator {
  return (targetClassPrototype, propertyKey) => {
    // PropClass=UserServiceInter伪接口类
    let PropServiceClass: any = Reflect.getMetadata("design:type",
      targetClassPrototype, propertyKey)
    //  在10-12代码基础上增加【开始】....
    let ServiceImplClass: any = PropServiceClass.getServiceImplClass();
    let ServiceImplInstance// 对象
    if (singleton) {// 如果是单件模式的对象的注入方式 
      ServiceImplInstance = ServiceImplClass.getInstance();
    } else {
      ServiceImplInstance = new ServiceImplClass();
    }
    //  在10-12代码基础上增加【结束】....
    //collectionInstance.set(propertyKey, PropClassObj);
    // 对比:Object.defineProperty
    // 好处:由于targetClassPrototype原型+propertyKey一起是绝对不会被覆盖的
    // 充分保证了数据属性中的value的对象的唯一性
    Reflect.defineProperty(targetClassPrototype, propertyKey,
      { value: ServiceImplInstance })// 修改为 PropServiceImplClassObj

  }
}

慕课网 TS 高级课程

10-23 【仿 Nestjs 装饰器实战】 依赖注入实现引发的深度思考+代码持续升级优化

依赖注入中的单件模式实现方式2实现

1 单件装饰器实现

import 'reflect-metadata'
type MyPropDecorator = (targetClassPrototype: any, propertyKey: string | symbol) => void

export default function Singleton(isSingleton: boolean): MyPropDecorator {
  return (targetClassPrototype, propertyKey) => {
    let PropServiceClass: any = Reflect.getMetadata("design:type",
      targetClassPrototype, propertyKey).
    let ServiceImplClass: any = PropServiceClass.getServiceImplClass();
    let ServiceImplInstanceOrClass// 对象
    let metaSingleton = Reflect.getMetadata("singleton",
      targetClassPrototype, propertyKey)

    if (isSingleton) {//如果是单件模式
      if (!metaSingleton) {// 第一次进来执行
        Reflect.defineMetadata("singleton", isSingleton, targetClassPrototype, propertyKey)
        ServiceImplInstanceOrClass = ServiceImplClass.getInstance();//单件模式获取唯一的对象
      } else {// 第二次或以上次重复进来执行
        console.log("单件模式创建,使用了上一次的对象");
      }
    } else {//如果不是单件模式,每次都创建一个对象
      ServiceImplInstanceOrClass = ServiceImplClass;
    }
    // 保存对象或者类
    Reflect.defineMetadata("ServiceImplInstanceOrClass",
      ServiceImplInstanceOrClass, targetClassPrototype, propertyKey)

  }
}

2 自动装配装饰器实现

import 'reflect-metadata'
import collectionInstance from '../collection'

type MyPropDecorator = (targetClassPrototype: any, propertyKey: string | symbol) => void
/**
 * 
 * @param dependencyid[injectid] 依赖id
 * @param singleton -判断是否是单件模式的对象 
 * @returns 
 */
export default function Autowired(dependencyid?: string, singleton?: boolean): MyPropDecorator {
  return (targetClassPrototype, propertyKey) => {
    let ServiceImplInstance: any
    let ServiceImplInstanceOrClass = Reflect.getMetadata("ServiceImplInstanceOrClass", targetClassPrototype, propertyKey)
    let metaSingleton = Reflect.getMetadata("singleton",
      targetClassPrototype, propertyKey)
    if (metaSingleton) {//如果是单件模式
      console.log("我是Autowired装饰器,单件模式获取对象");
      ServiceImplInstance = ServiceImplInstanceOrClass
    } else {
      ServiceImplInstance = new ServiceImplInstanceOrClass();
    }
    Reflect.defineProperty(targetClassPrototype, propertyKey,
      { value: ServiceImplInstance })// 修改为 PropServiceImplClassObj

  }
}

慕课网 TS 高级课程

10-24 【仿 Nestjs 装饰器实战】数据访问层和实体层封装

1 DAO 层 UserDaoImpl 类实现

import userinfosfrmdb from '../entity/UserInfo'
export default class UserDaoImpl {



  public findUsrByUsm(username: string, pwd: string) {
    return userinfosfrmdb.find((useinfo) => { return username === useinfo.username && pwd === useinfo.password })
  }

}

2 实体 层 Userinfo 实体类实现

export interface Userinfo {
  username: string,
  password: string,
  phone: string,
  role: string
  mark: string
}

// 数据表链接和本课程无关,但会模拟数据表数据操作
// 模拟Userinfo数据
let userinfosdb: Array<Userinfo> =
  [{
    username: "admin",
    password: "123",
    phone: "1111",
    role: "admin",
    mark: "管理员"
  },
  {
    username: "lisi",
    password: "123",
    phone: "1111",
    role: "general",
    mark: "开发工程师"
  },
  {
    username: "liuwu",
    password: "123",
    phone: "1111",
    role: "manager",
    mark: "项目精力"
  },
  ]

export default userinfosdb

3 业务逻辑层 UserService 类修改

import UserDaoImpl from '../dao/UserDaoImpl'
import { Userinfo } from '../entity/UserInfo'
export class UserServiceImpl {

  userDaoImpl: UserDaoImpl = new UserDaoImpl();
  static userServiceImpl: UserServiceImpl
  static getInstance() {
    if (!this.userServiceImpl) {
      this.userServiceImpl = new UserServiceImpl();
    }
    return this.userServiceImpl
  }

  constructor() {
    console.log("UserServiceImpl构造器....");
  }

  Login(username: string, pwd: string): Userinfo {
    console.log("进入service ...Login,username:", username)
    let userinfodb = this.userDaoImpl.findUsrByUsm(username, pwd)
    return userinfodb || null
  }
  register() {
    console.log("我是usersevice...register")
  }
}

慕课网 TS 高级课程

10-25 【仿 Nestjs 装饰器实战】控制器装饰器和请求方法装饰器实现

课程安排:分8步讲解

1. 环境搭建

1.1 安装包

 1 yarn add express -S
 2 yarn add @types/express -D
 3 yarn add express-session -S
 5 yarn add reflect-metadata -S
 6 yarn add typescript  -D
 7 yarn add nodemon -D

1.2 配置 package.json 脚本

  "scripts": {
    "app": "nodemon --watch src/ -e ts --exec ts-node ./src/expressapp.ts",
    "ctrl": "ts-node src/controller/HomeController.ts"
  }

1.3 修改 tsconfig.json 脚本

  // "strict": true 屏蔽strict
   "experimentalDecorators": true, // 开启            
   "emitDecoratorMetadata": true, // 开启
	

2. 编写工具类

// SessionUtil.ts 工具类
import { Request } from 'express'
export function getSession(req: Request) {
  return (req as any).session
}

3. Controller 装饰器实现

import 'reflect-metadata'
import { Request, Response } from 'express'
import { Autowired, Singleton, get, post, Controller } from '../decorator/'
import { UserServiceImpl, UserServiceInter } from '../service'
import CollectionInstance from '../collection/'
import { getSession } from '../util/SessionUtil';
import { Userinfo } from '../entity/UserInfo'
@Controller("/")
class UserController {

  @Autowired("userServiceImpl")//  修改Inject 为更专业的 Autowired 单词
  @Autowired("userServiceImpl")
  @Singleton(true)
  private userServiceImpl!: UserServiceInter // 修改Inject 为更专业的 Autowired 单词

  @get("/login")
  login(req: Request, res: Response): void {

    let htmlstr = `<div><form method="post" 
    action = "/loginprocess"><div>用户名: 
    <input type='text' name = 'username'/> </div><div>
     密码: <input type='password' name = 'pwd'/> </div>
     <div><input type="submit" value = "提交" /> </div>
     </form></div>`
    res.send(htmlstr);
  }


  @post("/loginprocess")
  loginprocess(req: Request, res: Response): void {

    console.log("loginprocess=this:", this);
    let session = getSession(req);

    let UserServiceImpl: UserServiceImpl =
      Reflect.getOwnPropertyDescriptor(UserController.prototype,
        "userServiceImpl").value//S100
    let userinfofrmdb: Userinfo = UserServiceImpl.Login(req.body.username, req.body.pwd)
    if (userinfofrmdb && userinfofrmdb.username)
      session.userinfofrmdb = userinfofrmdb
    // 基础复习:req.send只能发送一次,如果想发送多次,就必须使用res.write
    res.setHeader("Content-Type", "text/html;charset=UTF-8")
    let outputhtml = "";
    if (userinfofrmdb.role === "admin") {
      outputhtml += `<div>管理员:${userinfofrmdb.role}</div>`
      outputhtml += `<div><a href="/rights">进入管理员权限页面</a></div>`
    }
    res.write(outputhtml);
    res.write(`<div>登录成功,欢迎你:${userinfofrmdb.username}</div>`);
    res.write(`<div><a  href="/">进入首页</a></div>`);
    res.end();
  }
}

// let controller = new UserController();
// controller.test();
export { }

4. get 请求方法装饰器实现

// methoddecorator.ts文件
import 'reflect-metadata'

// 封装方法装饰器
export function get(path: string) {
  console.log("进入方法装饰器", " path为", path);
  return function (targetPrototype: any, methodname: string) {
    console.log("执行的类是:", targetPrototype, " 方法是:", methodname)
    Reflect.defineMetadata('path', path, targetPrototype, methodname)
  }
}

5. Controller 装饰器实现


import { router } from '../util/router'

type MyClassDecorator = <T extends { new(...args: any): any }>
  (targetClass: T) => any
export function Controller(reqRootPath: string): MyClassDecorator {
  return function (targetClass): any {
    console.log("控制器装饰器执行...");
    for (let methodname in targetClass.prototype) {
      let routerpath = Reflect.getMetadata("path", targetClass.prototype, methodname)

      // 拿到装饰器对应的方法
      const targetMethodfunc = targetClass.prototype[methodname];
      // S100理解:当执行对应routerpath时,会自动执行targetMethodfunc方法
      if (routerpath) {
        router.get(routerpath, targetMethodfunc);// S100
      }
    }
  }
}

6. 路由器提取

import { Router } from 'express'
export const router: Router = Router();

7. 增加装饰器二次导出

export * from './autowireddecortator'
export * from './methoddecorator'
export * from './singletondecorator'
export * from './controllerdecorator'

8 启动文件 app 实现

import 'reflect-metadata'
import express from 'express' 
import session from 'express-session'
// 引入控制器ts文件,会自动执行HomeController文件中方法装饰器@get和类装饰器@Controller
// 因为装饰器在/router/controlldecorators 这个文件中,
// 这一执行直接导致router增加了路由完成,就是controlldecorators的第S100行代码的执行
import './controller/UserController'

// 然后在引入路由器
import { router } from './router'

//Express4.16+已经在express包中加入了bodyParser,可直接作为express的方法使用.
const app = express();//Creates an Express application.

// 设置session关联的cookie信息
app.use(session({
  secret: "cookeid12345",
  name: "cookieinfo",
  resave: false,
  saveUninitialized: true,
}))

//   Express4.16+后的 处理表单数据 url 集成到了express框架中
app.use(express.urlencoded({ extended: false }))////处理表单数据 url
app.use(router)//添加路由到express应用对象-app对象中

let server = app.listen(5002, function () {
  console.log('node服务器启动,端口5002') //服务启动完成,输出日志
})

慕课网 TS 高级课程

10-26 【仿 Nestjs装饰器实战】多种请求方法装饰器实现

课程安排 1 升级方法装饰器 2 修改控制器装饰器 3 新建权限控制器类*

1. 升级方法装饰器

// methoddecorator.ts文件
import 'reflect-metadata'
import MethodType from '../util/MethodUtil';

// 封装方法装饰器
function reqMethodDecorator(methodType: MethodType) {
  return function (path: string) {
    return function (targetPrototype: any, methodname: string) {
      console.log("方法装饰器:执行的类是:", targetPrototype, " 方法是:", methodname)
      Reflect.defineMetadata('path', path, targetPrototype, methodname)
      Reflect.defineMetadata('methodtype', methodType, targetPrototype, methodname)
    }
  }
}

export const get = reqMethodDecorator("get")
export const post = reqMethodDecorator("post")

2 修改控制器装饰器

// controllclassdecorators.ts
import 'reflect-metadata'
import { RequestHandler } from 'express'

import MethodType from '../util/MethodUtil'

// 路由导入
import { router } from '../router'

// 通过装饰器实现项目路由功能
// 类装饰器
export function Controller(): MyClassDecorator {

  return function (targetClass) {
    //return function <T>(targetClass: { new(...args: any[]): T }) {
    Object.keys(targetClass.prototype).forEach((methodnamekey) => {

      // 请求路径
      let routerpath = Reflect.getMetadata("path", targetClass.prototype, methodnamekey)
      let methodtype: MethodType = Reflect.getMetadata("methodtype",
        targetClass.prototype, methodnamekey)

      // 拿到装饰器对应的方法
      const targetMethodfunc = targetClass.prototype[methodnamekey];
      // S100理解:当执行对应routerpath时,会自动执行targetMethodfunc方法
      if (routerpath && methodtype) {
        router[methodtype](routerpath, targetMethodfunc);
      }
    })
  }
}

10-27 【仿 Nestjs装饰器实战】多页面请求+中间件装饰器

课程安排: 7 步讲解

1 新建权限控制器类

// controller目录增加 RightsController.ts文件
import { Request, Response } from 'express'
import { get, Controller } from '../decorator/index'

@Controller()
export default class RightsControllers {

  @get("/rightsmanager")
  rightsShow(req: Request, res: Response) {
    console.log("rightShow")
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.write("管理员权限页面");
    res.write("<a href='javascript:history.back()'>返回</a>")
    res.send();
  }
}

2 在 UserController 类增加 index 函数

  @get('/')
  index(req: Request, res: Response): void {
    if (getSession(req).userinfofrmdb) {
      let htmlstr = `<div><a href='/searchFoodHistory' style='text-decoration:none;color:red'> 搜索美食历史信息 </a></div><div><a href = '/orderInfo'  style='text-decoration:none;color:red'> 订单信息 </a></div><div><a href="/loginout" style='text-decoration:none;color:red'>注销</a></div>`;
      res.send(htmlstr);
    } else {
      res.redirect("/login")
    }
  }

3 增加中间件装饰器

// decorator 目录 / methoddecorator.ts 文件
import { RequestHandler } from 'express'
import 'reflect-metadata'
export function middleware(middleware: RequestHandler) {
  return function (targetPrototype: any, key: string) {
    Reflect.defineMetadata("middleawares", middleware, targetPrototype, key)
  }
}

4 增加 FoodController 类

import { Request, Response } from 'express'
import {
  get, middleware, Controller
} from '../decorator';
import { isValidUser } from '../middleaware/middleawarefunc'
@Controller("/")
class FoodController {

  @get("/showFood")
  @middleware(isValidUser)
  showFood(req: Request, res: Response): void {
    res.setHeader("Content-Type", "text/html; charset=utf-8")
    res.write("大混沌");
    res.write("一锅炖")
    res.end();
  }
}

5 增加中间件

import { Request, Response, NextFunction } from 'express'
import { getSession } from '../util/sessionUtil';

export const isValidUser = (req: Request, res: Response,
  next: NextFunction) => {
  console.log("执行isValidUser...")
  let session = getSession(req);
  if (session.userinfofrmdb && session.userinfofrmdb.mark === "noallowlogin") {
    res.setHeader("Content-Type", "text/html; charset=utf-8")
    res.write("您是被禁人士,被限制访问");
    res.end();
  } else {
    next();
  }
}

6 修改 Controller 装饰器

import { router } from '../util/router'
import MethodType from '../util/methodtype'
import { RequestHandler } from 'express'
// 枚举

type MyClassDecorator = <T extends { new(...args: any): any }>
  (targetClass: T) => any
export function Controller(reqRootPath: string): MyClassDecorator {
  return function (targetClass): any {
    console.log("控制器装饰器执行...");
    for (let methodname in targetClass.prototype) {
      let routerpath = Reflect.getMetadata("path", targetClass.prototype, methodname)
      // 拿到装饰器对应的方法export const post = requestDecorator("post")
      let methodType: MethodType = Reflect.getMetadata("methodType", targetClass.prototype, methodname)
      const targetMethodfunc: RequestHandler = targetClass.prototype[methodname];
      // 获取中间件装饰器保存的中间件函数元数据
      let middleawre: RequestHandler = Reflect.getMetadata("middleawares",
      targetClass.prototype, methodname)

      // S100理解:当执行对应routerpath时,会自动执行targetMethodfunc方法
      if (routerpath && methodType) {
        if (middleawre) {
          router[methodType](routerpath, middleawre, targetMethodfunc)
        } else {
          router[methodType](routerpath, targetMethodfunc)
        }
      }
    }
  }
}

7 在启动文件中引入 RightsController 和 FoodController

import './controller/UserController'
import './controller/RightsController'
import './controller/FoodController'

10-28 【仿 Nestjs 装饰器实战】多个中间件装饰器实现

课程安排:讲解分4步进行

1. 在中间件文件中增加两个测试中间件函数

// 测试中间件函数1
export const SecondMiddleAware = (req: Request, res: Response,
  next: NextFunction) => {
  console.log("第二个中间件")
  next();
}

2 为 FoodController 增加第二个中间件装饰器修饰

import { Request, Response } from 'express'
import {
  get, middleware, Controller
} from '../decorator';
import { isValidUser, SecondMiddleAware } from '../middleaware/middleawarefunc'
@Controller("/")
class FoodController {

  @get("/showFood")
  @middleware(SecondMiddleAware)
  @middleware(isValidUser)
  showFood(req: Request, res: Response): void {
    res.setHeader("Content-Type", "text/html; charset=utf-8")
    res.write("大混沌");
    res.write("一锅炖")
    res.end();
  }
}

3. 修改中间件装饰器

// decorator 目录 / methoddecorator.ts 文件
import { RequestHandler } from 'express'
import 'reflect-metadata'
export function middleware(middleware: RequestHandler) {
  return function (targetPrototype: any, methodname: string) {
    // 方法1
    // 第一次进来的中间件函数middleware
    // let middlewares = Reflect.getMetadata("middleawares", targetPrototype, methodname)
    // if (!middlewares) {// 第一次进来的中间件函数数组赋值为初始值
    //   middlewares = []
    // }
    // 方法2
    let middlewares = Reflect.getMetadata("middleawares", targetPrototype, methodname) || []
    middlewares.push(middleware)
    Reflect.defineMetadata("middleawares", middlewares,
      targetPrototype, methodname)
  }
}

4. 修改控制器装饰器

import { router } from '../util/router'
import MethodType from '../util/methodtype'
import { RequestHandler } from 'express'
// 枚举

type MyClassDecorator = <T extends { new(...args: any): any }>
  (targetClass: T) => any
export function Controller(reqRootPath: string): MyClassDecorator {
  return function (targetClass): any {
    console.log("控制器装饰器执行...");
    for (let methodname in targetClass.prototype) {
      let routerpath = Reflect.getMetadata("path", targetClass.prototype, methodname)
      // 拿到装饰器对应的方法export const post = requestDecorator("post")
      let methodType: MethodType = Reflect.getMetadata("methodType", targetClass.prototype, methodname)
      const targetMethodfunc: RequestHandler = targetClass.prototype[methodname];
      // 获取中间件装饰器保存的中间件函数元数据

      let middleawres: RequestHandler[] = Reflect.getMetadata("middleawares",
        targetClass.prototype, methodname)

      // S100理解:当执行对应routerpath时,会自动执行targetMethodfunc方法
      if (routerpath && methodType) {
        if (middleawres) {
          router[methodType](routerpath, ...middleawres, targetMethodfunc)
        } else {
          router[methodType](routerpath, targetMethodfunc)
        }
      }
    }
  }
}

10-29 【仿 Nestjs装饰器实战】解析路由器和请求参数对象底层复杂泛型

10-30 【仿 Nestjs装饰器实战后的深度思考题+作业】控制器根路径实现优化依赖注入对象调用

课程安排: 课后思考题 + 一个作业

深度思考题

我们知道在 HomeController 中 获取 userService 是按照下面第 S100 行代码这样获取的,但实际开发时我们希望看到更彻底的依赖注入, 如何不写 S100 行就能直接获取 userService 对象 ,用更简洁的方式来调用。

loginprocess(req: Request, res: Response): void {

    console.log("loginprocess=this:", this);
    let session = getSession(req);
    let UserServiceImpl: UserServiceImpl =
  Reflect.getOwnPropertyDescriptor(UserController.prototype,"userServiceImpl").value  // S100
    let userinfofrmdb: Userinfo = UserServiceImpl.Login(req.body.username, req.body.pwd)

}

作业:如果 控制器装饰器增加了根路径 比如: controller("/home"),那又该如何处理请求路径?

@Controller("/usermodule")
class UserController {

  //@Autowired("userServiceImpl")//  修改Inject 为更专业的 Autowired 单词
  @Autowired("userServiceImpl")
  @Singleton(true)
  private userServiceImpl!: UserServiceInter // 修改
  .....
}