Tutorials
Quickstart

快速开始 Quickstart

在软件工程中,控制反转(IoC)和依赖注入(DI)是实现松耦合设计的重要原则和技术。本章节将演示如何在 TypeScript 中通过注解能力使用 IoC 和 DI。

技术原理

在TypeScript中,我们可以利用装饰器(Decorator)功能来创建自定义注解,实现IoC容器和依赖注入机制。

控制反转(IoC)

控制反转是一种设计原则,它改变了对象的获取依赖的方式。传统编程模式下,对象自行创建或查找所需的依赖对象,而在IoC中,对象的创建和依赖的查找由外部容器负责,对象只是声明其所需的依赖项。

依赖注入(DI)

依赖注入是实现IoC的一种方法,通过构造器、属性或方法将依赖关系注入到对象中,而不是由对象自身创建依赖。DI可以达到以下目标:

  • 降低组件间的耦合度
  • 增强组件的可测试性
  • 提高代码的可维护性和扩展性

内置工具

Artus 中的 Core 实现底层基于 IoC(控制反转,Inversion of Control)实现,便于在上层框架中集成 DI(依赖注入,Dependency Injection)、AOP(面向切面编程,Aspect Oriented Programming)等特性(详见 loader 章节)。

为了便于用户使用,由 @artus/core@artusx/koa 等包,提供对应的 decorator,简化在 typescript 中使用。

@artus/core

  • Injectable
  • Inject

@artusx/koa

  • HTTPController
  • Middleware
  • POST / GET

使用示例

此处我们提供一个基于 koa 实现的 http-server 的简单示例,可点击链接查看完整代码。

https://github.com/artusjs/artusx/tree/main/packages/apps/artusx-koa (opens in a new tab)

Service

通过添加 Injectable 注解,可将对应的 class 注入到框架内置的容器中。

在 Core 中按照 Scope 提供 IOC Container 作为基础对象池,Scope 包括:

  • Singleton(单例生命周期,全局执行一次实例化)
  • Execution(执行生命周期,每次执行时实例化)
  • Transient(临时生命周期,每次使用时实例化)
service/api.ts
import { Inject, Injectable, ArtusInjectEnum, ArtusApplication } from '@artus/core';
 
@Injectable()
export default class APIService {
  @Inject(ArtusInjectEnum.Application)
  app: ArtusApplication;
 
  async mockApi() {
    return {
      data: {
        name: 'artusx',
      },
    };
  }
}

Controller

通过 HTTPController 相关注解,可将 route 以及 method 注入到 koa-router;基于依赖注入的原则,在 controller 中,可通过 Inject 注解,将依赖的 service 注入。

Inject 支持通过 string 以及 clazz 方式在容器中查找依赖,可参考示例中的 config / service 的使用。

controller/api.ts
import { ArtusInjectEnum, Inject } from '@artus/core';
import { GET, HTTPController } from '../types';
import type { ArtusxContext } from '../types';
import APIService from './api.service';
 
@HTTPController('/api')
export default class APIController {
  @Inject(ArtusInjectEnum.Config)
  config: Record<string, string | number>;
 
  @Inject(APIService)
  apiService: APIService;
 
  @GET('/')
  async home(ctx: ArtusxContext) {
    ctx.body = 'home';
  }
 
  @GET('/info')
  async getInfo(ctx: ArtusxContext) {
    ctx.body = await this.apiService.mockApi();
  }
}