快速开始
简介
本库是利用 typescript 的 decorator 实现了依赖注入能力的小型工具库。
实现过程参考了 Spring, Angular, typedi, InversifyJS等类似的开源实现。
整体上可以将本库看作 InversifyJS 的 tiny 版本。详细对比参考这里。
开发本库的最初目的是作为use-vue-service的基础依赖,提供轻量级依赖注入能力。
本项目默认是单例模式,可以配置为inTransientScope模式。没有inversify的inRequestScope模式。
安装
npm install @kaokei/di本库 不依赖 reflect-metadata,所以 不需要 安装这个 npm 包。
本库依赖 TypeScript 环境,使用 Stage 3 装饰器规范(TC39 标准)。
注意:
因为本库使用 Stage 3 装饰器,所以 不需要experimentalDecorators: true。
因为本库不依赖装饰器元数据,所以也 不需要emitDecoratorMetadata: true。
基本使用
下面例子展示了container.get(CountService)可以快速获取实例化的 countService 对象,并且自动完成了 logger 属性的注入。
import { Container, Inject, Injectable } from '@kaokei/di';
class LoggerService {
public log(...msg: any[]) {
console.log('from logger service ==>', ...msg);
}
}
@Injectable()
class CountService {
public count = 0;
@Inject(LoggerService)
private logger: LoggerService;
public addOne() {
this.count++;
this.logger.log('addOne ==> ', this.count);
}
}
// 实例化一个容器对象
const container = new Container();
// 绑定 CountService
container.bind(CountService).toSelf();
// 绑定 LoggerService
container.bind(LoggerService).toSelf();
// 通过容器获取 CountService 类的实例对象
// 并且本库会自动注入 LoggerService 类的实例对象作为 CountService 的依赖
const countService = container.get(CountService);
// 注意到我们并没有手动的维护 LoggerService 的注入过程
// 但是 this.logger.log 依然可以打印出相应的日志
countService.addOne();下面的例子展示了手动 new 获取 countService 实例化对象,此时是没有 logger 属性的。
所以还需要手动 new 一个 loggerService 对象,并赋值给 countService.logger 属性,到这里才算是最终完成了 countService 实例化过程。
整体上来看,上下两种代码在功能上是等价的,但是显然上方代码是有container自动完成依赖的注入是更加方便的。
class LoggerService {
public log(...msg: any[]) {
console.log('from logger service ==>', ...msg);
}
}
class CountService {
public count = 0;
public logger: LoggerService;
public addOne() {
this.count++;
this.logger.log('addOne ==> ', this.count);
}
}
// 手动实例化CountService对象
const countService = new CountService();
// 手动实例化LoggerService对象,并赋值给logger属性
countService.logger = new LoggerService();
// 此时可以正常调用addOne方法,this.logger.log可以打印出相应的日志
countService.addOne();复杂案例
下面展示了一个复杂的依赖关系图,其中包含 12 个类(A ~ L),它们之间存在复杂的依赖关系,并且包含循环依赖。
在这种复杂依赖关系中,如果需要手动创建一个类A的实例对象,那么就需要手动创建剩下的所有11个类(B ~ L),并且需要手动维护它们之间的依赖关系,也就是需要手动赋值相应的属性。
相反如果是通过container来获取类A的实例对象,那么就非常简单了,只需要在每一个类中通过@Inject声明所有依赖的其他类,再通过container.get(A)就可以获取完整的类A的实例对象了。
A
/ | \
B C D
/| | |\
E F G H I
/| |
J K L
| |
F G
| |
B ←──→ C (循环依赖:B ↔ C)依赖关系说明:
- A 依赖 B、C、D(根节点,入口类)
- B 依赖 E、F,同时 B 依赖 C(形成 B ↔ C 循环依赖)
- C 依赖 G,同时 C 依赖 B(形成 C ↔ B 循环依赖)
- D 依赖 H、I
- E 依赖 J、K
- F 依赖 B(形成 B → E → J → F → B 循环依赖链)
- G 依赖 L(形成 G ↔ L 循环依赖)
- L 依赖 G(形成 L ↔ G 循环依赖)
- J 依赖 F