【Nestjs实操】服务依赖注入

在开始学习之前,我们首先准备下开发环境:

  • Node:16.20.2
  • 包管理器:pnpm
  • nestjs版本:10.2.1
  • 全局安装nestjs命令行:pnpm add -g @nestjs/cli

一、初始化项目

执行nest new nestjs-blog,系统会自动创建一个nestjs项目,并安装好好所有依赖项目。

image-20240309102023372

初始的项目目录结构如下:

image-20240309102445492

然后我们执行pnpm start:dev启动项目,访问localhost:3000,就可以看到项目已经正常启动。

image-20240309102832939

文件说明:

  • /src/main:项目入口文件。用于启动整个项目,可以配置
  • /src/app.module.ts:模块。整个项目可以划分为多个独立的模块,作为每个模块的入口文件,负责引入其他模块、注入服务、导出服务、配置控制器。app.module.ts作为整个项目的基石模块,还负责引入项目中所有的模块、服务、控制器。
  • /src/app.service.ts:服务。用于存放本模块中的所有业务逻辑
  • /src/app.controller.ts:控制器。用于配制路由、鉴权、入参、返回数据等

二、依赖注入

1、服务注入

最常见的就是本模块中的服务,在模块中注入,然后在控制器中声明并引用。这里就拿app.module.ts文件举例:

//app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],//注入AppService服务
})
export class AppModule {}
//app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  //在构造函数中直接声明
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}
//app.service.ts
import { Injectable } from '@nestjs/common';
//服务中必须要使用Injectable装饰器,声明此服务可以被注入
@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

2、自定义服务名称

app.module.ts文件中,注入服务时,上面的方式是直接在providers中配置服务类型就可以了,系统会自动到 服务注册容器 中去检索对应的服务。如下:

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],//直接写服务类名称
})

完整的写法是这样的:

@Module({
  imports: [],
  controllers: [AppController],
  providers: [{
    provide: AppService,	//声明服务名称
    useValue: new AppService(),//声明服务使用的服务类
  }],
})

当然我们也可以自定义服务名,如下:

//app.module.ts
@Module({
  imports: [],
  controllers: [AppController],
  providers: [{
    provide: "app",//这里改成字符串名称
    useValue: new AppService(),
  }],
})

//app.controller.ts
export class AppController {
  constructor(@Inject("app") private readonly appService: AppService) { }

  ...
}

可以看到在app.module.ts中声明了服务字符串名称:provide: "app",然后在控制器中调用时,就需要在构造函数入参中添加@Inject("app")

3、注入自定义数据

useValue中返回的数据可以是自定义的任何数据。

//app.module.ts
@Module({
  imports: [],
  controllers: [AppController],
  providers: [{
    provide: "app",
    useValue: { name: "IT飞牛" },//自定义数据
  }],
})

//app.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';

@Controller()
export class AppController {
  constructor(@Inject("app") private readonly appService: any) { }

  @Get()
  getHello(): string {
    return this.appService.name;//返回自定义数据中的属性值
  }
}

再次访问页面localhost:3000,页面显示如下:

image-20240309130802190

4、使用工厂函数注入服务

有些场景下,我们需要根据条件动态引入服务,这时候我们可以借助useFactory工厂函数,来动态加载。

//app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';

//配置服务
const configService = {
  provide: "config",
  useValue: { url: "11.22.33.44" },
}

@Module({
  imports: [],
  controllers: [AppController],
  providers: [configService, {
    provide: "app",
    inject: ["config"],//引入配置服务config,这样可以给useFactor作为入参使用
    useFactory: (config) => {
        //合并config配置服务,并且追加appUrl属性。
      return {
        ...config,
        appUrl: "itfeiniu.com"
      }
    }
  }],
})
export class AppModule { }

//app.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';

@Controller()
export class AppController {
  constructor(@Inject("app") private readonly appService: any) { }

  @Get()
  getHello(): string {
      //appService服务实例中,使用useFactory引入时,混合了config和自定义追加的appUrl。这里都可以调用。
    return "url:" + this.appService.url + " <br/> appUrl:" + this.appService.appUrl;
  }
}

使用工厂函数来动态加载服务时,我们可以使用injest引入其他服务,然后就可以在工厂函数中写业务逻辑,返回我们需要的服务数据。上述例子中,app

服务最终返回的是:

{
    url: "11.22.33.44",
    appUrl: "itfeiniu.com"
}

所有我们在控制器中可以直接调用。

工厂函数常见用于,根据环境变量的不同,动态引入不同环境下对应的服务数据。

5、异步引入服务

服务在注册的时候,我们也可以使用异步引入,useFactory工厂函数返回一个Promise对象。具体如下:

//app.module.ts
@Module({
  imports: [],
  controllers: [AppController],
  providers: [configService, {
    provide: "app",
    inject: ["config"],
    useFactory: async (config) => {
        //返回一个Promise对象
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve({
            ...config,
            appUrl: "itfeiniu.com"
          });
        }, 3000);
      })
    }
  }],
})

通过后端服务运行日志,可以看到,服务加载耗时3s

image-20240309133104475

6、共享服务

一个服务,我们有时候需要在不同的模块中使用,那么就需要使用服务共享。

  • 创建一个test服务,执行如下代码,创建test.module.tstest.service.ts文件:

    nest g mo test
    nest g s test --no-spec
    

    创建后,会自动引入app.module.ts

    image-20240309140354881

  • test.service.ts代码

    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class TestService {
        constructor() { }
        getTest(): string {
            return 'Hello Test!';
        }
    }
    
  • test.module.ts代码

    import { Module } from '@nestjs/common';
    import { TestService } from './test.service';
    
    @Module({
        providers: [TestService],
        exports: [TestService]//不光要在app.module.ts的import中引入,还需要在这里导出
    })
    export class TestModule { }
    
  • app.module.ts代码:

    @Module({
      imports: [TestModule],//使用nest g s test命令行创建后,会自动添加;否则需要手动添加。
      controllers: [AppController],
      ...
    })
    
  • app.controller.ts代码:

    import { Controller, Get, Inject } from '@nestjs/common';
    import { TestService } from './test/test.service';
    
    @Controller()
    export class AppController {
      constructor(private testService:TestService) { }
    
      @Get()
      getHello(): string {
        return this.testService.getTest();//调用test服务中的getTest方法,返回:Hello Test!
      }
    }
    

配置完上述代码,我们再次访问:localhost:3000页面如下:

image-20240309140911471

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT飞牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值