Skip to content

依赖注入(Dependency Injection)

核心概念

awe-axios 中,依赖注入(DI)通过 @Inject 装饰器与 IoC(控制反转)容器实现,用于解耦组件间的依赖关系,自动管理实例的创建和注入过程。以下是详细实现方式和使用说明:

基本使用

@Inject 支持多种配置方式,满足不同场景的依赖查找需求:

字符串表达式注入

格式:[模块名.]别名(模块名可选,默认模块为 __default__

typescript
// 注入默认模块的 "userService"
@Inject('userService')
private userService: UserService;

// 注入 "api" 模块的 "productApi"
@Inject('api.productApi')
private productService: ProductService;

配置对象注入

当然你也可以通过配置对象来实现注入查找,配置对象结构为:

ts
type GetInstanceConfig = {
  /**
   * 模块名
   */
  module?: string | symbol;
  /**
   * 别名
   */
  alias?: string;
  /**
   * 构造器
   */
  ctor?: DecoratorClass;
  /**
   * 创建实例模式
   */
  scope: InstanceScope | string;
  /**
   * 备份列表
   */
  backups?: InjectBackups;
};

如下示例:

typescript
// 通过类查找(最常用,类型安全)
@Inject({ ctor: UserService })
private userService: UserService;

// 通过模块+别名查找
@Inject({ module: 'api', alias: 'productApi' })
private productService: ProductService;

当同时配置了 ctoralias

如果你同时配置了 ctoraliasawe-axios会只使用 module + ctor 来进行查找,并忽略 alias

关于scopebackups

scope 配置控制依赖实例的创建策略,默认值为 SINGLETONbackups 配置指定备选实例。后续会详细讲解

构造函数直接注入

当然你也可以直接传入类作为参数(等价于 { ctor: 类 }):

typescript
@Inject(UserService)
private userService: UserService;

实例作用域

awe-axios在实例注入时提供了多种实例注入方式,包括单例模式、瞬时模式、原型模式、浅克隆模式等,这些模式可以满足不同场景的需求。通过 scope 配置控制依赖实例的创建策略,默认值为 SINGLETON

作用域说明适用场景
SINGLETON单例模式,全局唯一实例工具类、配置服务
TRANSIENT瞬时模式,每次注入创建新实例有状态的对象、请求上下文
PROTOTYPE原型模式,基于原实例创建新对象需要继承原实例状态的场景
SHALLOWCLONE浅克隆模式,复制原实例的表层属性简单对象的快速复制
DEEPCLONE深克隆模式,完全复制原实例复杂对象的独立副本

瞬时模式

瞬时模式下每次注入都会创建一个新的实例,awe-axios默认采用瞬时模式注入,如下示例:

typescript
@Component()
class User {}
class UserService {
  @Inject(User)
  user1!: User;
  @Inject({
    module: '__default__',
    alias: 'user',
    scope: 'TRANSIENT',
  })
  user2!: User;
}
let userService = new UserService();
// 由于默认采用的瞬时模式,所以两个注入的 user 实例是不同的
console.log(userService.user1 === userService.user2); // false

单例模式

单例模式下每次注入都会返回同一个实例,如下示例:

typescript
@Component()
class User {}
class UserService {
  @Inject({
    ctor: User,
    scope: 'SINGLETON',
  })
  user1!: User;
  @Inject({
    module: '__default__',
    alias: 'user',
    scope: 'SINGLETON',
  })
  user2!: User;
}
let userService = new UserService();
// 由于默认采用的单例模式,所以两个注入的 user 实例是同一个
console.log(userService.user1 === userService.user2); // true

原型模式

原型模式下每次注入都会创建一个以该类实例为原型的新实例,如下示例:

ts
@Component()
class User {}
class UserService {
  @Inject({
    ctor: User,
    scope: 'PROTOTYPE',
  })
  user1!: User;
}
let userService = new UserService();
// user1 以 User类实例为原型
console.log(userService.user1 instanceof User); // true
console.log(Object.getPrototypeOf(userService.user1)); // User {}

浅克隆模式

浅克隆模式下每次注入都会创建一个新的实例,但只复制原实例的表层属性,如下示例:

ts
@Component()
class User {
  obj: any = { a: 1 };
}
class UserService {
  @Inject({
    ctor: User,
    scope: 'SINGLETON',
  })
  user1!: User;
  @Inject({
    ctor: User,
    scope: 'SHALLOWCLONE',
  })
  user2!: User;
}
let userService = new UserService();
// 改变user1的obj中的a属性
userService.user1.obj.a = 2;
// 由于采用的是浅克隆,所以user2的obj中的a属性也会随之改变
console.log(userService.user2.obj.a); // 2

深克隆模式

深克隆模式下每次注入都会创建一个新的实例,但会递归复制原实例的所有属性,如下示例:

ts
@Component()
class User {
  obj: any = { a: 1 };
}
class UserService {
  @Inject({
    ctor: User,
    scope: 'SINGLETON',
  })
  user1!: User;
  @Inject({
    ctor: User,
    scope: 'DEEPCLONE',
  })
  user2!: User;
}
let userService = new UserService();
// 改变user1的obj中的a属性
userService.user1.obj.a = 2;
// 由于采用的是深克隆,所以user2的obj中的a属性不会随之改变
console.log(userService.user2.obj.a); //1

依赖备选

为了保证注入的稳定性,awe-axios提供了依赖备选机制,当主实例查找失败时,可通过 backups 配置指定备选实例,backups 配置可以是一个构造器或一个对象或构造器和对象组成的数组,如下示例:

typescript
class User {
  obj: any = { a: 1 };
}
class Student {}
class UserService {
  @Inject({
    alias: 'person',
    backups: [User, Student],
    // 或
    // backups: [new User(), new Student()]
    // 或
    // backups: User
  })
  user1!: User;
}
let userService = new UserService();
// 没有person实例,所以采用备用User类作为实例 User { obj: { a: 1 } }
console.log(userService.user1);

备选顺序

当备选实例有多个时,awe-axios会按注册顺序依次尝试,直到找到实例为止。如果没有则注入 undefined