在VSCode架构中 InstantiationService 扮演着重要角色:

  1. 依靠注入容器InstantiationService充当了一个依靠注入容器,办理着VSCode中的各种服务和组件之间的依靠联系。它经过供给服务的注册和解析机制,使得不同的组件能够以松耦合的办法协同工作。
  2. 服务实例化:InstantiationService担任实例化各种服务,并确保它们的依靠联系得到满意。它经过运用ServiceCollection作为注册表,记载服务的类型和完结类之间的映射联系。当需求获取某个服务实例时,InstantiationService会依据注册表中的信息,实例化并回来相应的服务目标。
  3. 解决依靠联系:InstantiationService能够自动解析和处理服务之间的依靠联系。当一个服务依靠于其他服务时,InstantiationService会自动递归地实例化并满意一切的依靠联系,确保每个服务都能获取到它所需求的依靠项。
  4. 插件系统支撑:InstantiationServiceVSCode的插件系统供给了强大的支撑。插件能够经过InstantiationService获取所需的服务,并在运行时动态注册和刊出服务。这为插件的开发和扩展供给了便利和灵活性。

假如想阅读VSCode源码的话,InstantiationService 是不得不先去了解的模块,它是各种服务之间的桥,理解了它,才能更好的把握各种服务之间的联系。

Tip: 为了便利调试和阅读代码,我将VSCode中InstantiationService的源码部分,单独提取出来,放在了仓库里。

ServiceCollection

InstantiationService中,ServiceCollection用于存储服务的注册信息。它是一个简单的键值对调集,其中键是服务的标识符,值能够是实例目标SyncDescriptor描绘符。

import { ServiceIdentifier } from './instantiation';
import { SyncDescriptor } from './descriptor';
​
export class ServiceCollection {
​
  private _entries = new Map<ServiceIdentifier<any>, any>();
​
  constructor(..._entries: [ServiceIdentifier<any>, any][]) {
    for (const [id, service] of _entries) {
      this.set(id, service);
     }
   }
​
  set<T>(id: ServiceIdentifier<T>, instanceOrDescriptor: T | SyncDescriptor<T>): T | SyncDescriptor<T> {
    const result = this._entries.get(id);
    this._entries.set(id, instanceOrDescriptor);
    return result;
   }
​
  has(id: ServiceIdentifier<any>): boolean {
    return this._entries.has(id);
   }
​
  get<T>(id: ServiceIdentifier<T>): T | SyncDescriptor<T> {
    return this._entries.get(id);
   }
​
  print() {
    console.log("Collections", this._entries.keys());
    console.log(JSON.stringify(this._entries));
   }
}

ServiceCollection供给了一组简单的办法,用于增加、获取和查看服务的注册信息。

ServiceIdentifier

ServiceIdentifier 是一个函数类型,用于表示 服务的标识符

同时它也是一个装饰器,用于装饰目标的结构函数的服务参数

InstantiationService 中扮演着索引依靠的重要角色,就像它的函数名称是 id 相同,首要用于与建立与注册的服务实例的依靠联系

export interface ServiceIdentifier<T> {
   (...args: any[]): void;
  type: T;
}
export namespace _util {
  export const serviceIds = new Map<string, ServiceIdentifier<any>>();
​
  export const DI_TARGET = "$di$target";
  export const DI_DEPENDENCIES = '$di$dependencies';
​
  export function getServiceDependencies(ctor: any): { id: ServiceIdentifier<any>; index: number }[] {
    return ctor[DI_DEPENDENCIES] || [];
   }
}
export function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {
  if (_util.serviceIds.has(serviceId)) {
    return _util.serviceIds.get(serviceId)!;
   }
  const id = <any>function (target: Function, key: string, index: number): any {
    if (arguments.length !== 3) {
      throw new Error("@IServiceName-decorator can only be used to decorate a parameter'")
     }
    storeServiceDependency(id, target, index);
   }
  id.toString = () => serviceId;
  _util.serviceIds.set(serviceId, id);
  return id;
}

SyncDescriptor

SyncDescriptor 服务描绘器,用于指定服务的类型和详细完结类。

type Ctor<T> = new (...args: any[]) => T;
​
export class SyncDescriptor<T> {
  readonly ctor: Ctor<T>;
  readonly staticArguments: any[];
  readonly supportsDelayedInstantiation: boolean;
​
  constructor(ctor: Ctor<T>, staticArguments: any[] = [], supportsDelayedInstantiation: boolean = false) {
    this.ctor = ctor;
    this.staticArguments = staticArguments;
    this.supportsDelayedInstantiation = supportsDelayedInstantiation;
   }
}

SyncDescriptor供给了结构函数、静态参数和推迟实例化支撑的才能。

它经过结构函数的办法将服务的完结类与依靠联系绑定在一起。

它带来的一个优点是,关于需求注入的服务,无需在外部实例化,只需求运用SyncDescriptor对服务目标进行描绘,即可。

实例化和依靠注入

InstantiationService类具有两种实例化目标并完结依靠注入的办法:注入服务实例注入服务描绘器(SyncDescriptor)

注入服务实例

需求被注入的服务Service1Service2Service3,都是提早初始化服务实例,而后增加到服务调集中,以供InstantiationService能够经过服务标识符进行索引服务实例,进行依靠注入。

class TargetWithStaticParam {
  constructor(v: boolean, @IService1 service1: IService1) {
    assert.ok(v);
    assert.ok(service1);
    assert.strictEqual(service1.c, 1);
   }
}
​
const collection = new ServiceCollection();
const service = new InstantiationService(collection);
collection.set(IService1, new Service1());
collection.set(IService2, new Service2());
collection.set(IService3, new Service3());
​
service.createInstance(TargetWithStaticParam, true);

实例化完结部分部分

  1. 经过服务标识符,在服务调集中获取服务实例
  2. 处理结构函数中的静态参数和注入的服务参数,初始化目标
private _createInstance<T>(ctor: any, args: any[] = [], _trace: Trace): T {
​
    // arguments defined by service decorators
    const serviceDependencies = _util.getServiceDependencies(ctor).sort((a, b) => a.index - b.index);
    // 结构函数的依靠注入服务列表
    const serviceArgs: any[] = [];
    for (const dependency of serviceDependencies) {
   // 经过服务标识符获取服务实例
      const service = this._getOrCreateServiceInstance(dependency.id, _trace);
      if (!service) {
        this._throwIfStrict(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`, false);
      }
      serviceArgs.push(service);
    }
​
    const firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length;
​
    // 正常来说,结构函数中注入的服务目标实例,总是在静态参数的后边排布,比如:constructor(val1, val2, @IService1 val3: IService1)
  // 我们将 val1 和 val2 称为静态参数 
  // 假如创立实例时,第一个注入服务下标跟传入的静态参数长度不一致,则需求调整
    if (args.length !== firstServiceArgPos) {
      const delta = firstServiceArgPos - args.length;
      if (delta > 0) { // 假如传入的参数少于结构函数的静态参数,中心缺失的参数,用空弥补
        args = args.concat(new Array(delta));
      } else { // 假如传入的参数多于结构函数的静态参数,则删去位置为firstServiceArgPos极端之后的参数
        args = args.slice(0, firstServiceArgPos);
      }
    }
​
    // 依据结构函数和参数,创立目标实例
    return Reflect.construct<any, T>(ctor, args.concat(serviceArgs));
  }

注入服务实例的特点是:需求运用者提早实例化需求注入的服务。

注入服务描绘器(SyncDescriptor)

注入服务实例不同的是,实例化目标时,能够选择传入服务描绘器。

这种办法,需求注入的服务目标,无需被手动实例化,InstantiationService 内部会依据注入的服务描绘器,进行实例化。

const Service1 = createDecorator<any>('service1');
class Service1Impl {
  constructor(@IInstantiationService instant: IInstantiationService) {
    const c = instant.invokeFunction(accessor => accessor.get(Service2))
    assert.ok(c);
   }
}
const Service2 = createDecorator<any>("service2");
class Service2Impl {
  constructor() { }
}
const Service21 = createDecorator<any>('service21');
class Service21Impl {
  constructor(@Service2 public readonly service2: Service2Impl, @Service1 public readonly service1: Service1Impl) { }
}
const instant = new InstantiationService(new ServiceCollection(
   [Service1, new SyncDescriptor(Service1Impl)],
   [Service2, new SyncDescriptor(Service2Impl)],
   [Service21, new SyncDescriptor(Service21Impl)]
))
const obj = instant.invokeFunction(accessor => accessor.get(Service21));

实例化完结部分

  1. 构建注入服务的依靠联系图(graph)
  2. 遍历依靠联系图,依据服务描绘器,创立服务实例,并存储到 _services: ServiceCollection

遍历依靠联系图,是以广度优先办法,优先找到依靠联系图中的根节点(最远节点,也即最外层的服务目标)

Service21Impl
├── Service2Impl
└── Service1Impl
    └── InstantiationService

例子中,Service21Impl联系图中最外层根节点则是Service2InstantiationService

/**
* 经过执行invokeFunction,能够得到拜访依靠注入的服务句柄,如accessor.get(xx)
* @param fn 
* @param args 
* @returns 
*/
invokeFunction<R, TS extends any[] = []>(fn: (accessor: ServicesAccessor, ...args: TS) => R, ...args: TS): R {
 const _trace = Trace.traceInvocation(this._enableTracing, fn);
 let _done = false; // 执行完结标记,回调函数一旦完毕,accessor不允许再被拜访
 try {
  const accessor: ServicesAccessor = {
   get: <T>(id: ServiceIdentifier<T>) => {
​
    if (_done) {
     throw new Error('service accessor is only valid during the invocation of its target method');
     }
​
    const result = this._getOrCreateServiceInstance(id, _trace);
    if (!result) {
     throw new Error(`[invokeFunction] unknown service '${id}'`);
     }
    return result;
    }
   };
  return fn(accessor, ...args);
  } finally {
  _done = true;
  _trace.stop();
  }
}
/**
* 创立服务实例,并缓存到服务实例调集中
*/
private _createAndCacheServiceInstance<T>(id: ServiceIdentifier<T>, desc: SyncDescriptor<T>, _trace: Trace): T {
​
    type Triple = { id: ServiceIdentifier<any>; desc: SyncDescriptor<any>; _trace: Trace };
    const graph = new Graph<Triple>(data => data.id.toString());
​
    let cycleCount = 0;
    const stack = [{ id, desc, _trace }];
    /**
     * 深度优先遍历,构建服务的SyncDescriptor依靠联系图
     * 因为SyncDescriptor只是类的描绘符,不是实例,所以根依据联系图,在需求时,递归创立依靠联系图中一切的服务实例据
     */
    while (stack.length) {
      // 从当前服务节点开端
      const item = stack.pop()!;
      // 假如不存在,则刺进图中
      graph.lookupOrInsertNode(item);
​
      if (cycleCount++ > 1000) {
    // 检测到循环依靠联系,抛出异常
        throw new CyclicDependencyError(graph);
      }
​
      // 查看一切的依靠项是否存在,确定是否需求先创立他们 
      for (const dependency of _util.getServiceDependencies(item.desc.ctor)) {
        // 在服务调集(_services)中获取实例(service instance)或者描绘目标(service SyncDescriptor)
        const instanceOrDesc = this._getServiceInstanceOrDescriptor(dependency.id);
        if (!instanceOrDesc) {
          this._throwIfStrict(`[createInstance] ${id} depends on ${dependency.id} which is NOT registered.`, true);
        }
​
        // 记载服务之间的依靠联系
        this._globalGraph?.insertEdge(String(item.id), String(dependency.id));
        // 假如目标是描绘目标,则将节点刺进的图中,并入栈,进行深度查找
        if (instanceOrDesc instanceof SyncDescriptor) {
          const d = { id: dependency.id, desc: instanceOrDesc, _trace: item._trace.branch(dependency.id, true) };
          graph.insertEdge(item, d);
          stack.push(d);
        }
      }
    }
​
    while (true) {
      /**
       * 广度优先遍历
       * 递归遍历根节点,由外向内的对联系图中的描绘符,创立实例目标
       */
      const roots = graph.roots();
      if (roots.length === 0) {
        if (!graph.isEmpty()) {
     // 没有根节点但图中仍然存在节点,阐明存在循环依靠联系,抛出异常
          throw new CyclicDependencyError(graph);
        }
        break;
      }
​
      for (const { data } of roots) {
        // 查看服务的SyncDescriptor是否存在,并可能触发递归实例化
        const instanceOrDesc = this._getServiceInstanceOrDescriptor(data.id);
        if (instanceOrDesc instanceof SyncDescriptor) {
           // 创立实例并覆盖服务调集中的实例
          const instance = this._createServiceInstanceWithOwner(data.id, data.desc.ctor, data.desc.staticArguments, data.desc.supportsDelayedInstantiation, data._trace);
          this._setServiceInstance(data.id, instance);
        }
        // 创立完结后,移出根节点,进入下一次查找
        graph.removeNode(data);
      }
    }
    return <T>this._getServiceInstanceOrDescriptor(id);
  }
​
private _createServiceInstance<T>(id: ServiceIdentifier<T>, ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace: Trace): T {
  if (!supportsDelayedInstantiation) {
    // 假如不支撑推迟实例化,则立即创立实例
    return this._createInstance(ctor, args, _trace);
   } else {
    const child = new InstantiationService(undefined, this._strict, this, this._enableTracing);
    child._globalGraphImplicitDependency = String(id);
​
    // 创立一个由闲暇值支撑的代理目标。
    // 这个战略是在闲暇时间或实际需求时实例化服务,而不是在注入到消费者时实例化。
    // 当服务尚未实例化时,回来"空事情"。
    const earlyListeners = new Map<string, LinkedList<Parameters<Event<any>>>>();
​
    const idle = new IdleValue<any>(() => {
      const result = child._createInstance<T>(ctor, args, _trace);
​
      // 将之前保存的早期监听器订阅到实际的服务上
      for (const [key, values] of earlyListeners) {
        const candidate = <Event<any>>(<any>result)[key];
        if (typeof candidate === 'function') {
          for (const listener of values) {
            candidate.apply(result, listener);
           }
         }
       }
      earlyListeners.clear();
​
      return result;
     });
​
    // 创立一个代理目标,它由闲暇值支撑
    return <T>new Proxy(Object.create(null), {
      get(target: any, key: PropertyKey): any {
        if (!idle.isInitialized) {
          // 判别是否为事情的拜访
          if (typeof key === 'string' && (key.startsWith('onDid') || key.startsWith('onWill'))) {
            let list = earlyListeners.get(key);
            if (!list) {
              list = new LinkedList();
              earlyListeners.set(key, list);
             }
            // 回来一个事情函数,当事情触发时会将监听器加入到行列中
            const event: Event<any> = (callback, thisArg, disposables) => {
              const rm = list!.push([callback, thisArg, disposables]);
              return toDisposable(rm);
             };
            return event;
           }
         }
​
        // 判别值是否已存在
        if (key in target) {
          return target[key];
         }
​
        // 创立值
        const obj = idle.value;
        let prop = obj[key];
        if (typeof prop !== 'function') {
          return prop;
         }
        prop = prop.bind(obj);
        target[key] = prop;
        return prop;
       },
      set(_target: T, p: PropertyKey, value: any): boolean {
        // 设置值
        idle.value[p] = value;
        return true;
       },
      getPrototypeOf(_target: T) {
        return ctor.prototype;
       }
     });
   }
}