原文链接

InversityJS 是一个 IoC 框架。IoC(Inversion of Control) 包括依赖注入(Dependency Injection) 和依赖查询(Dependency Lookup)。

相比于类继承的方式,控制反转解耦了父类和子类的联系。

  1. import 'reflect-metadata'
  2. import { inject, injectable, Container } from 'inversify'
  3. const container = new Container()
  4. @injectable()
  5. class PopMusic {
  6. getName() {
  7. return '流行音乐'
  8. }
  9. }
  10. container.bind('request1').to(PopMusic)
  11. @injectable()
  12. class ClassicalMusic {
  13. getName() {
  14. return '古典音乐'
  15. }
  16. }
  17. container.bind('request2').to(ClassicalMusic)
  18. @injectable()
  19. class Music {
  20. pm: any
  21. cm: any
  22. constructor(
  23. @inject('request1') popMusic: any,
  24. @inject('request2') classicalMusic: any) {
  25. this.pm = popMusic
  26. this.cm = classicalMusic
  27. }
  28. getName() {
  29. const result = this.pm.getName() + this.cm.getName()
  30. return result
  31. }
  32. }
  33. container.bind('Plan').to(Music)
  34. const music: any = container.get('Plan')
  35. console.log(music.getName()) // 流行音乐古典音乐

上述案例可以抽象为下图:

虚线表示可以注入,但在代码中没有表现出来。

代码流程可概括如下:

1.将所有相关类(这里指 Music、popMusic、classicMusic) 通过 @injectable 声明进 container 容器;

2.通过 container.get() 获取 container.bind().to(target) 中的目标对象(这里指 Music);

3.如果目标对象中的 constructor() 里有 @inject(), 则将相应的实例(这里指 PopMusic 与 classicalMusic 的实例)当作构造函数的参数’注入’;

inject 源码简化如下:

  1. // 这是一个属性装饰器
  2. function inject(serviceIdentifier) {
  3. return function (target, targetKey) {
  4. const metadataValue = { [targetKey]: [Metadata { key: 'inject', value: serviceIdentifier })] }
  5. Reflect.defineMetadata('inversify:tagged_props', metadataValue, target.constructor);
  6. }
  7. }

injectable 源码简化如下:

  1. // 这是一个类装饰器
  2. function injectable() {
  3. return function (target) {
  4. const metadataValue = []
  5. Reflect.defineMetadata('inversify:paramtypes', metadataValue, target)
  6. return target
  7. }
  8. }

从简化版源码中可以看到 inject/injectable 最终是对 Reflect.defineMetadata() 的一个使用。可以将 metadata 看成是一种相对高效的数据结构。

InversityJS 深度结合了 reflect-metadata, reflect-metadata 在 Reflect 基础上对其 api 进行了扩展。

metadata 本质上是一个 WeakMap 对象。扩展:Map 和 WeakMap 的区别

Reflect.defineMetadata(metadataKey, metadataValue, target[, propertyKey]) 简化版实现如下:

  1. const Metadata = new WeakMap()
  2. function defineMetadata(metadataKey, metadataValue, target, propertyKey) {
  3. metadataMap = new Map()
  4. metadataMap.set(metadataKey, metadataValue)
  5. targetMetadata = new Map()
  6. targetMetadata.set(propertyKey, metadataMap)
  7. Metadata.set(target, targetMetadata)
  8. }

Reflect.getOwnMetadata(metadataKey, target[, propertyKey]) 简化版实现如下:

  1. function getOwnMetadata(metadataKey, target, propertyKey) {
  2. var targetMetadata = Metadata.get(target)
  3. var metadataMap = targetMetadata.get(propertyKey)
  4. return metadataMap.get(metadataKey)
  5. }

其数据结构可表示如下:

  1. WeakMap {
  2. target: Map {
  3. propertyKey: Map {
  4. metadataKey: metadataValue
  5. }
  6. }
  7. }

版权声明:本文为MuYunyun原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/MuYunyun/p/9922888.html