当前位置: 首页 > article >正文

nestjs-属性注入和构造函数注入的区别

在 NestJS 中,依赖注入的工作机制如下:

  1. 构造函数注入:

NestJS 在实例化类时,会首先解析构造函数中的依赖,并注入相应的实例。

因此,构造函数中的依赖在类实例化时就已经准备好了。

  1. 属性注入:

属性注入是通过 @Inject() 装饰器实现的。

NestJS 会在类实例化之后,再为属性赋值。

因此,在构造函数中访问属性注入的依赖时,属性还未被赋值,所以是 undefined

只有在构造函数执行完成后,属性才会被正确注入。

在你的场景中:

你在 controller 中通过属性注入 Reflector。

在构造函数中打印 this.reflector 时,Reflector 还未被注入,所以是 undefined。

在后续的请求方法(如 get 方法)中,Reflector 已经被注入,所以可以正常访问。

代码示例
以下是一个示例代码,展示了这个问题:

复制
import { Controller, Get, Inject } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Controller('test')
export class TestController {
  @Inject(Reflector)
  private reflector: Reflector;

  constructor() {
    console.log('Constructor:', this.reflector); // undefined
  }

  @Get()
  test() {
    console.log('GET Method:', this.reflector); // Reflector 实例
    return 'Hello World';
  }
}

解决方案

  1. 使用构造函数注入(推荐)
    NestJS 官方推荐使用构造函数注入,因为它能确保依赖在类实例化时就已经准备好,避免了属性注入的时机问题。

import { Controller, Get } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Controller('test')
export class TestController {
  constructor(private readonly reflector: Reflector) {
    console.log('Constructor:', this.reflector); // Reflector 实例
  }

  @Get()
  test() {
    console.log('GET Method:', this.reflector); // Reflector 实例
    return 'Hello World';
  }
}
  1. 延迟访问属性注入的依赖
    如果你必须使用属性注入,可以在构造函数中避免直接访问属性注入的依赖,而是在后续方法中访问。
import { Controller, Get, Inject } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Controller('test')
export class TestController {
  @Inject(Reflector)
  private reflector: Reflector;

  constructor() {
    // 不要在构造函数中访问 this.reflector
  }

  @Get()
  test() {
    console.log('GET Method:', this.reflector); // Reflector 实例
    return 'Hello World';
  }
}
  1. 使用生命周期钩子
    如果你需要在类实例化后立即访问属性注入的依赖,可以使用 NestJS 的生命周期钩子(如 OnModuleInit)。
import { Controller, Get, Inject, OnModuleInit } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Controller('test')
export class TestController implements OnModuleInit {
  @Inject(Reflector)
  private reflector: Reflector;

  onModuleInit() {
    console.log('onModuleInit:', this.reflector); // Reflector 实例
  }

  @Get()
  test() {
    console.log('GET Method:', this.reflector); // Reflector 实例
    return 'Hello World';
  }
}

总结
属性注入的依赖在构造函数中不可用,因为 NestJS 会在类实例化之后才为属性赋值。

推荐使用构造函数注入,这是 NestJS 的标准做法,能避免时机问题。

如果必须使用属性注入,可以在生命周期钩子(如 OnModuleInit)或后续方法中访问注入的依赖。

nestjs推荐使用构造函数注入,如果作为父类,那么构造函数无法注入(只能通过子类在构造函数调用 super(...)传递参数),此时可以使用属性 @Inject() 注入.

Reflect.getMetadata() 和 reflector 的使用场景

  1. 在使用属性注入的类的构造函数中,推荐使用 Reflect.getMetadata,因为此时属性注入的实例等到所在类实例化后才能获取,如果是通过构造函数注入,两者则没有区别

reflect-metadata的原理

reflect-metadata的源码很简单,就是给Reflect对象增加了几个方法,并且用WeakMap存储key为target,值为Map,该Map里存储key为对象的property,值为Map,最里层的map就是存储元数据键值对,如

  • Reflect.defineMetadata(‘key’,value,target,property?) 给target变量或者其target的property属性定义键为key的元数据value
  • Reflect.getMetadata(‘key’,target,property?) 获取target或target.property(可以通过原型获取)键为key的元数据

只能给对象或者对象和key或者函数(函数也是对象)定义元数据

import "reflect-metadata";

class A {
  methodA() {}
}
const a = new A();
const metadatKey = "metadatKey";

// 给实例方法添加元数据
Reflect.defineMetadata(metadatKey, "a.methodA元数据", a.methodA);
console.log(Reflect.getMetadata(metadatKey, a.methodA));

function fn() {}
const b = {};
const primitiveVar = ""; // 会报错,因为target只允许是对象或者函数(函数也是对象,源码中就是这样判断的)

Reflect.defineMetadata(metadatKey, "fn元数据", fn);
Reflect.defineMetadata(metadatKey, "b元数据", b);
Reflect.defineMetadata(metadatKey, "primitiveVar元数据", primitiveVar);

console.log(Reflect.getMetadata(metadatKey, fn)); //fn元数据
console.log(Reflect.getMetadata(metadatKey, b)); //b元数据
console.log(Reflect.getMetadata(metadatKey, primitiveVar)); // 报错

如下所示
在这里插入图片描述


http://www.kler.cn/a/548905.html

相关文章:

  • 通用知识库问答流程
  • 通过操作系统中的IO模型理解Java中的BIO,NIO,AIO
  • Python入门全攻略(六)
  • 小米 R3G 路由器(Pandavan)实现网络打印机功能
  • 在conda环境下,安装Pytorch和CUDA
  • 【第11章:生成式AI与创意应用—11.4 生成式AI在其他领域的创新应用与未来展望】
  • 基于阿里云可观测产品构建企业级告警体系的通用路径与最佳实践
  • 深入解析 vLLM:高性能 LLM 服务框架的架构之美(上)
  • React Hooks 的两个坑点
  • 【第15章:量子深度学习与未来趋势—15.1 量子计算基础与量子机器学习的发展背景】
  • 基于Spring Boot的大学校园生活信息平台的设计与实现(LW+源码+讲解)
  • C# 变量,字段和属性的区别
  • Web后端 - Maven管理工具
  • 【第10章:自然语言处理高级应用—10.4 NLP领域的前沿技术与未来趋势】
  • 【C++】34.智能指针(1)
  • STM32 如何使用DMA和获取ADC
  • PerfMonitor高效处理器性能监控与分析利器
  • 374_C++_升级等其他类型标签,使用将4字节字符串转换为无符号整数的定义方式
  • 免费好用的DeepSeek平台汇总(持续更新...)
  • 动态库与静态库:深入解析与应用