Theia 服务与贡献

在本节中,我们将描述 Theia 扩展如何使用平台和其他扩展提供的服务。此外,我们描述了扩展如何通过贡献点(贡献点,是个Theia的专有名词,表示你自己写的扩展,能对外贡献什么功能)为 Theia 工作台做出贡献。

服务是一个对象,可以为它的消费者提供功能。服务与其消费者之间的契约由接口描述。服务的任何实现都必须根据接口文档实现该接口。 Theia 中的任何扩展都可以提供和/或使用服务。 Theia 平台提供的扩展提供了一组默认服务,例如消息服务。但是,您也可以提供和使用您自己的自定义服务。

贡献点定义了钩子。贡献点通过一些接口来定义,贡献者必须实现这些接口,例如CommandContribution。然后定义贡献点的扩展将获取贡献,例如将贡献的命令添加到 Theia 工作台。(这句原文是:The extension defining the contribution point will then pick up the contribution, e.g. adding the contributed command to the Theia workbench. 没明白,翻译的不太明白)

贡献点,如服务,可以由任何扩展贡献和定义。 Theia 平台定义了一组默认贡献点,例如将命令或菜单添加到 Theia 工作台。但是,您也可以定义自己的。

服务和贡献点的使用需要扩展之间的通信。Theia 使用依赖注入来避免扩展之间的依赖。

 
在以下部分中,我们将简要介绍依赖注入、服务、贡献以及如何定义贡献点。
 

依赖注入 (DI)
Theia 使用 DI 框架 InversifyJS 来连接不同的服务和贡献点。

依赖注入将服务的消费者(即消费者的依赖)与这些服务的实际创建和获取分离。例如,如果你想使用一个服务,你既不需要实例化它,也不需要从某个地方手动获取它。相反,依赖注入容器在创建组件时注入服务。依赖注入容器为你解析依赖,如果有必要,甚至可以即时实例化它。这样,服务的消费者就不必担心它们来自哪里,并且您可以在以后轻松地交换服务的实际实现,而无需更改消费者。依赖注入容器基于您在启动时通过所谓的容器模块提供的一些配置工作。

我们将在下面的“服务”和“贡献贡献点”部分提供有关如何使用依赖注入的示例。

依赖注入是 Theia 的一个组成部分。因此,我们强烈建议至少学习 InversifyJS (http://inversify.io/)的基础知识。有关更多详细信息,请参阅这篇关于依赖注入如何在 Theia 中工作的文章(https://eclipsesource.com/blogs/2018/11/28/how-to-inversify-in-eclipse-theia/)

使用服务
要在 Theia 中使用服务,您可以使用 DI 将其作为依赖项注入。依赖项通常通过您要获取的服务的接口指定。这样,您甚至可以避免依赖于任何特定实现,调用者只知道接口。这允许提供实现的组件无缝替换服务的实现。您甚至可以在不破坏任何服务使用者的情况下覆盖服务的现有实现。

要获取从依赖注入容器中注入的参数,您需要使用标识符(字符串)对其进行注释。另一方面,服务提供商也将使用标识符发布可用服务。当通过依赖注入请求具有特定标识符的参数时,依赖注入上下文将查找它并返回相应服务的实例。为方便起见,服务提供者通常使用 Symbol 作为标识符,该标识符与相应的服务接口本身具有完全相同的名称。下面的例子中,'@inject(MessageService)' 是符号(服务标识符),而'private readonly messageService: MessageService' 是指服务的接口。

服务,或者更一般地说,依赖项可以作为在构造函数或初始化函数中的参数注入(参见下面的代码示例)。

// Injection in the constructor.
constructor(@inject(MessageService) private readonly messageService: MessageService) { }
 
// Injection as a field.
@inject(MessageService)
protected readonly messageService!: MessageService;
 
// Injection in an initialization function (will be called after the constructor and after injecting fields.
@postConstruct()
protected async init(@inject(MessageService) private readonly messageService: MessageService) { }

请注意,注入仅适用于由 DI 容器创建的组件。 因此,它们必须用@injectable 标记(参见下面的代码示例)。 此外,它们必须在 DI 上下文中注册(示例见下一节)。

@injectable()
export class MyContribution implements SomeContributionInterface

贡献点

Theia 中的贡献点定义了一个要实现的接口,例如 CommandContribution。 贡献扩展必须提供此接口的实现并用@injectable 标记它,例如:

mycommand-contribution.ts

@injectable()
export class MyCommandContribution implements CommandContribution

此外,贡献必须绑定在 DI 容器中,以便贡献点提供者可以获取我们的贡献,更准确地说是将其注入。 在扩展的容器模块中完成绑定。 它将实现绑定到贡献接口,或者正准确的说,是绑定到表示接口的符号上(参见下面的示例)。

helloworld-frontend-module.ts

export default new ContainerModule(bind => {
   // add your contribution bindings here
   bind(CommandContribution).to(HelloworldCommandContribution);
...
});

有关服务和贡献点的使用的简单示例,请参阅 https://theia-ide.org/docs/commands_keybindings/。

 

定义贡献点 

如果一个扩展想要为其他人提供一个钩子来贡献,他们应该定义一个贡献点。 贡献点只是许多其他人可以实现的接口。 扩展将在需要时委托给他们。

例如,OpenerService 定义了一个贡献点以允许其他人注册 OpenHandlers。 你可以看看这里的代码 https://github.com/eclipse-theia/theia/blob/master/packages/core/src/browser/opener-service.ts。

Theia 已经提供了大量的贡献点列表。 查看存在哪些贡献点的一个好方法查找引用了 bindContributionProvider 的地方。

 

服务贡献者

贡献提供者基本上是贡献的容器,其中贡献是绑定类型的实例。

这是非常通用的。

要将类型绑定到贡献提供者,您可以这样做:

(来自messaging-module.ts)

export const messagingModule = new ContainerModule(bind => {
    bind<BackendApplicationContribution>(BackendApplicationContribution).to(MessagingContribution);
    bindContributionProvider(bind, ConnectionHandler)
});

最后一行将 ContributionProvider 绑定到包含所有 ConnectionHandler 绑定实例的一个。

它是这样使用的:

(From messaging-module.ts)

    constructor( @inject(ContributionProvider) @named(ConnectionHandler) protected readonly handlers: ContributionProvider<ConnectionHandler>) {
    }

在这里我们注入了一个 ContributionProvider ,它具有之前由 bindContributionProvider 绑定的命名 ConnectionHandler 值。

这使任何人都可以绑定 ConnectionHandler,现在,当消息模块启动时,所有 ConnectionHandler 都将被初始化。

 

posted @ 2022-07-15 17:22  theiaide  阅读(477)  评论(0编辑  收藏  举报