跳到主要内容
版本:Next

装饰器

本节介绍用于提供类元数据的 Inversify 装饰器。

injectable

用于设置类元数据的装饰器,以便容器可以接收类发出的元数据。

强烈建议使用 @injectable 装饰器注释作为服务提供的每个类。但是,并非在每种情况下都是强制性的。

参数

injectable 装饰器接受一个可选的 scope 参数,允许你定义绑定实例的生命周期:

@injectable('Singleton') // or 'Transient' or 'Request'
class MyService {
// ...
}

有效的作用域值为:

  • 'Singleton':容器将仅创建一个实例,并为所有请求返回该实例。
  • 'Transient':容器将为每个请求创建一个新实例。
  • 'Request':容器将为每个唯一请求创建一个实例(在具有请求作用域的 Web 应用程序中很有用)。

什么时候 injectable 是强制性的?

每当预期有类发出的元数据时。

考虑以下示例代码:

import { injectable } from 'inversify';

@injectable()
class B {
readonly foo: string = 'foo';
}

@injectable()
class A {
constructor(public readonly b: B) {}
}

启用 emitDecoratorMetadata 选项的 CommonJS 转译可能如下所示:

Object.defineProperty(exports, "__esModule", { value: true });
const inversify_1 = require("inversify");
let B = class B {
foo = 'foo';
};
B = __decorate([
(0, inversify_1.injectable)()
], B);
let A = class A {
b;
constructor(b) {
this.b = b;
}
};
A = __decorate([
(0, inversify_1.injectable)(),
__metadata("design:paramtypes", [B])
], A);

当且仅当目标类应用了任何类装饰器时,TypeScript 才会发出类元数据。如果我们从 A 中删除 @injectable,则转译后的代码看起来会有很大不同:

Object.defineProperty(exports, "__esModule", { value: true });
class B {
foo = 'foo';
}
class A {
b;
constructor(b) {
this.b = b;
}
}

这一次,即使启用了 emitDecoratorMetadata TypeScript 选项,也不会发出类元数据,从而导致执行时出现问题。

inject

用于在构造函数参数或类属性与服务 ID 之间建立关系的装饰器。

解析类的实例时,目标构造函数参数或属性将以与 container.get 相同的方式解析。

示例:装饰类构造函数参数

@injectable()
class Ninja {
constructor(
@inject(weaponServiceId)
public readonly weapon: Weapon,
) {}
}

const container: Container = new Container();

container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);

const ninja: Ninja = container.get(Ninja);

// Returns 10
const ninjaWeaponDamage: number = ninja.weapon.damage;

示例:装饰属性

@injectable()
class Ninja {
@inject(weaponServiceId)
public readonly weapon!: Weapon;
}

const container: Container = new Container();

container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);

const ninja: Ninja = container.get(Ninja);

// Returns 10
const ninjaWeaponDamage: number = ninja.weapon.damage;

injectFromBase

用于从基类注入依赖项的装饰器。子类注入优先于基类注入。

参数

injectFromBase 装饰器接受一个可选的 InjectFromBaseOptions 对象,具有以下属性:

  • extendConstructorArguments (boolean,默认为 true):如果为 true,将注入基类的构造函数参数。
  • extendProperties (boolean,默认为 true):如果为 true,将注入基类的属性。
  • lifecycle (object,可选):用于扩展基类生命周期方法的生命周期选项:
    • extendPostConstructMethods (boolean,默认为 true):如果为 true,将扩展基类的 post-construct 方法。
    • extendPreDestroyMethods (boolean,默认为 true):如果为 true,将扩展基类的 pre-destroy 方法。

示例:装饰基类构造函数参数

type Weapon = unknown;

@injectable()
abstract class BaseSoldier {
public weapon: Weapon;
constructor(@inject('Weapon') weapon: Weapon) {
this.weapon = weapon;
}
}

@injectable()
@injectFromBase({
extendConstructorArguments: true,
extendProperties: false,
})
class Soldier extends BaseSoldier {}

// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);

示例:装饰基类属性

type Weapon = unknown;

@injectable()
abstract class BaseSoldier {
@inject('Weapon')
public weapon: Weapon;
}

@injectable()
@injectFromBase({
extendConstructorArguments: false,
extendProperties: true,
})
class Soldier extends BaseSoldier {}

// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);

injectFromHierarchy

用于从完整的类层级结构(所有祖先)注入依赖项的装饰器。注入是自上而下应用的(从最远的祖先到最近的基类)。

参数

injectFromHierarchy 装饰器接受一个可选的 InjectFromHierarchyOptions 对象,具有以下属性:

  • extendConstructorArguments (boolean,默认为 true):如果为 true,将注入所有祖先的构造函数参数。
  • extendProperties (boolean,默认为 true):如果为 true,将注入所有祖先的属性。
  • lifecycle (object,可选):用于扩展层级结构生命周期方法的生命周期选项:
    • extendPostConstructMethods (boolean,默认为 true):如果为 true,将扩展所有祖先的 post-construct 方法。
    • extendPreDestroyMethods (boolean,默认为 true):如果为 true,将扩展所有祖先的 pre-destroy 方法。

示例:装饰层级结构构造函数参数

type Weapon = unknown;

@injectable()
abstract class BaseSoldier {
public weapon: Weapon;
constructor(@inject('Weapon') weapon: Weapon) {
this.weapon = weapon;
}
}

@injectable()
abstract class IntermediateSoldier extends BaseSoldier {}

@injectable()
@injectFromHierarchy({
extendConstructorArguments: true,
extendProperties: false,
})
class Soldier extends IntermediateSoldier {}

// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);

示例:装饰层级结构属性

type Weapon = unknown;

@injectable()
abstract class BaseSoldier {
@inject('Weapon')
public weapon: Weapon;
}

@injectable()
abstract class IntermediateSoldier extends BaseSoldier {}

@injectable()
@injectFromHierarchy({
extendConstructorArguments: false,
extendProperties: true,
})
class Soldier extends IntermediateSoldier {}

// Returns a soldier with a weapon
const soldier: Soldier = container.get(Soldier);

multiInject

用于在构造函数参数或类属性与服务 ID 之间建立关系的装饰器。

解析类的实例时,目标构造函数参数或属性将以与 container.getAll 相同的方式解析。

参数

multiInject 装饰器接受一个可选的 MultiInjectOptions 对象,具有以下属性:

  • chained (boolean):启用跨容器层级结构的链式解析的选项。有关更多信息,请参阅 文档

示例:装饰属性

@injectable()
class Ninja {
@multiInject(weaponServiceId)
public readonly weapon!: Weapon[];
}

const container: Container = new Container();

container.bind(Ninja).toSelf();
container.bind(weaponServiceId).to(Katana);

const ninja: Ninja = container.get(Ninja);

// Returns 10
const ninjaWeaponDamage: number | undefined = ninja.weapon[0]?.damage;

named

用于在构造函数参数或类属性与元数据名称之间建立关系的装饰器。

@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject('Weapon') @named('melee') katana: Weapon,
@inject('Weapon') @named('ranged') shuriken: Weapon,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}

const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenNamed('melee');
container.bind<Weapon>('Weapon').to(Shuriken).whenNamed('ranged');
container.bind(Ninja).toSelf();

const ninja: Ninja = container.get(Ninja);

optional

用于确定目标构造函数参数或属性是可选的装饰器,因此,如果未找到关联服务 ID 的绑定,则不应解析它。

@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon | undefined;
constructor(
@inject('Katana') katana: Weapon,
@inject('Shuriken') @optional() shuriken: Weapon | undefined,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}

const container: Container = new Container();
container.bind<Weapon>('Katana').to(Katana);

container.bind(Ninja).toSelf();

// Returns a "Ninja" instance with a "Katana" katana property and undefined shuriken property.
const ninja: Ninja = container.get(Ninja);

postConstruct

用于为目标类建立激活处理程序的装饰器。有关更多信息,请参阅 文档

interface Weapon {
damage: number;
}

export class Katana implements Weapon {
#damage: number = 10;

public get damage(): number {
return this.#damage;
}

@postConstruct()
public improve(): void {
this.#damage += 2;
}
}

const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);

// Katana.damage is 12
const katana: Weapon = container.get<Weapon>('Weapon');

preDestroy

用于为目标类建立停用处理程序的装饰器。有关更多信息,请参阅 文档

interface Weapon {
damage: number;
}

class Katana implements Weapon {
readonly #damage: number = 10;

public get damage(): number {
return this.#damage;
}

@preDestroy()
public onDeactivation(): void {
console.log(`Deactivating weapon with damage ${this.damage.toString()}`);
}
}

const container: Container = new Container();

container.bind<Weapon>('Weapon').to(Katana).inSingletonScope();

container.get('Weapon');

await container.unbind('Weapon');

tagged

用于在构造函数参数或类属性与元数据标签之间建立关系的装饰器。

@injectable()
class Ninja {
public katana: Weapon;
public shuriken: Weapon;
constructor(
@inject('Weapon') @tagged('weaponKind', 'melee') katana: Weapon,
@inject('Weapon') @tagged('weaponKind', 'ranged') shuriken: Weapon,
) {
this.katana = katana;
this.shuriken = shuriken;
}
}

const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenTagged('weaponKind', 'melee');
container
.bind<Weapon>('Weapon')
.to(Shuriken)
.whenTagged('weaponKind', 'ranged');
container.bind(Ninja).toSelf();

const ninja: Ninja = container.get(Ninja);

unmanaged

用于确定 Inversify 不应注入目标构造函数参数或属性的装饰器。

import { Container, injectable, unmanaged } from 'inversify';

@injectable()
class Base {
public prop: string;
constructor(@unmanaged() arg: string) {
this.prop = arg;
}
}

@injectable()
class Derived extends Base {
constructor() {
super('inherited-value');
}
}

const container: Container = new Container();

container.bind(Derived).toSelf();

const derived: Derived = container.get(Derived);

// Returns 'inherited-value'
const derivedProp: string = derived.prop;