在VSCode架构中 InstantiationService
扮演着重要角色:
- 依靠注入容器:
InstantiationService
充当了一个依靠注入容器,办理着VSCode中的各种服务和组件之间的依靠联系。它经过供给服务的注册和解析机制,使得不同的组件能够以松耦合的办法协同工作。 - 服务实例化:
InstantiationService
担任实例化各种服务,并确保它们的依靠联系得到满意。它经过运用ServiceCollection
作为注册表,记载服务的类型和完结类之间的映射联系。当需求获取某个服务实例时,InstantiationService
会依据注册表中的信息,实例化并回来相应的服务目标。 - 解决依靠联系:
InstantiationService
能够自动解析和处理服务之间的依靠联系。当一个服务依靠于其他服务时,InstantiationService
会自动递归地实例化并满意一切的依靠联系,确保每个服务都能获取到它所需求的依靠项。 - 插件系统支撑:
InstantiationService
为VSCode
的插件系统供给了强大的支撑。插件能够经过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)
注入服务实例
需求被注入的服务Service1
、Service2
和Service3
,都是提早初始化为服务实例
,而后增加到服务调集中,以供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);
实例化完结部分部分
- 经过
服务标识符
,在服务调集
中获取服务实例
- 处理结构函数中的静态参数和注入的服务参数,初始化目标
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));
实例化完结部分
- 构建注入服务的依靠联系图(graph)
- 遍历依靠联系图,依据服务描绘器,创立服务实例,并存储到
_services: ServiceCollection
中
遍历依靠联系图,是以广度优先办法,优先找到依靠联系图中的根节点(最远节点,也即最外层的服务目标)
Service21Impl
├── Service2Impl
└── Service1Impl
└── InstantiationService
例子中,Service21Impl
联系图中最外层根节点则是Service2
和 InstantiationService
。
/**
* 经过执行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;
}
});
}
}