“插件是为系统提供功能的代码和/或数据的结构化包。可以以代码库(带有公共 [应用程序接口] API 的 Java 类)、平台扩展甚至文档的形式来提供功能。插件可以定义扩展点、定义良好的位置,其他插件可以在这些位置添加功能。”
Eclipse使用OSGi作为插件系统的基础。动态添加新插件和停止现有插件的能力。以动态方式管理组件生命周期的一个健壮的系统。
OSGi是基于Java的框架,旨在用于需要长运行时间、动态更新和对运行环境破坏最小的系统。
OSGi规范定义了绑定包生命周期的基础架构和绑定包的交互方式。这些规则通过使用特殊 Java类加载器来强制执行。
在一般Java应用程序中,CLASSPATH中的所有类都对所有其他类可见。相反,OSGi类加载器基于OSGi规范和每个绑定包的manifest.mf文件中指定的选项来限制类交互。
在3.1之前版本的Eclipse中,在每个插件的plugin.xml文件中定义插件依赖关系以及扩展和扩展点。在使用OSGi的新版本Eclipse中,依赖关系信息被分解到manifest.mf文件中,而 plugin.xml文件只包含扩展和扩展点的XML定义。
插件级的依赖关系改为需要显式导出和导入包的依赖关系。插件开发人员必须进行专门选择来使插件中的功能可供外部使用。该限制允许内部包保留在内部,避免插件暴露不必要的类。
Eclipse的扩展和扩展点 可插拔
每一个希望被别的模块扩展的模块,都必须声明一系列的扩展点,即插座;希望在这个模块上扩展功能的模块,需要按照扩展点的什么来编写扩展,即插头。扩展点提供服务,扩展是要服务。
延迟装载,只有在一个插件被其他模块调用的时候,才装载到内存中。
通过将扩展的声明和实现分离,eclipse实现类延迟装载。
扩展点和扩展的声明都是通过XML文件完成的,即清单文件MANIFEST.MF,描述了一个插件能够做什么,而JAVA代码则具体完成这些功能。系统启动时,只需搜索清单文件,建立一张索引表,知道有哪些插件以及能够提供什么服务。当eclipse第一次启动时,eclipse运行时会遍历plugins文件夹中的目录,扫描每个插件的清单文件信息,并建立一个内部模型来记录它所找到的每个插件的信息。
RCP包括基于OSGi的运行时框架Equniox,基于SWT/JFace的图形模块,eclipse平台的UI和Runtime模块。基于Eclipse的应用程序所需的最小插件集称为Eclipse Rich Client Platform(RCP)
Platform Runtime 平台运行库是内核,它在启动时检查已安装了哪些插件,并创建关于它们的注册表信息。即在eclipse运行时发现和管理插件。为降低启动时间和资源使用,它在实际需要任何插件时才加载该插件。除了内核外,其他每样东西都是作为插件来实现的。
Workspace 工作区是负责管理用户资源的插件。这包括用户创建的项目、那些项目中的文件,以及文件变更和其他资源。工作区还负责通知其他插件关于资源变更的信息,比如文件创建、删除或更改。
Workbench 工作台为Eclipse提供用户界面。它是使用标准窗口工具包(SWT)和一个更高级的 API(JFace)来构建的;SWT 是 Java 的 Swing/AWT GUI API 的非标准替代者,JFace 则建立在 SWT 基础上,提供用户界面组件。
插件可以扮演双重角色,其他插件服务的使用者和其他插件服务的提供者。
manifest.mf
Bundle-Activator
该类用于启动和停止绑定包。该类扩展
org.eclipse.core.runtime.Plugin,实现了 BundleActivator 接口。
Bundle-Version
该属性指定绑定包的版本号。包导入和必需的绑定包规范可以包括绑定包版本号。
Export-Package
该属性指定要公共暴露给其他插件的所有包。
Import-Package
该属性指定要从必需插件中显式导入的所有包。默认情况下,必须为要启动的绑定包解析所有包。还可以将包导入指定为可选项,以支持包不存在的情况。显式导入的类在 Require-Bundle 插件中的包之前解析。
Require-Bundle
该属性指定要在给定绑定包中导入使用的绑定包及其已导出的包。指定的绑定包在显式包导入之后解析。
---------------------- --------------------
|plugin A | |plugin B |
| --------------- | contibute | -------------- |
| | ext point p | <--------------- | extension | |
| --------------- |_ | -------------- |
| || | | | || |
| --------------- | |implement | -------------- |
| | interface I | <--|------------ | class C | |
| --------------- | | | -------------- |
---------------------- | ----------/ -------
| create, call |
----------------------|
你可以把extension point想为接口, 而我们扩展这些extension,其实就是实现了这个接口
假设plugin A定义了一个extension point,
plugin B定义了一个extension,是基于plugin A的这个extension point的,它的实现类是class C,那eclipse启动后, 会读取每个plugin的配置plugin.xml,然后发现plugin B有一个基于ext point p的扩展,那它就会用interface I作为对象, 然后实例化一个class C, 就等于实现了这个extension
eclipse的内部实现
IPluginRegistry registry = Platform.getPluginRegistry();
IExtensionPoint extensionPoint = registry.getExtensionPoint(xpid);
//通过扩展点ID获得扩展点
IExtension[] extensions = extensionPoint.getExtensions(); //获得该扩展点的所有扩展
// For each extension ...
for (int i = 0; i < extensions.length; i++) {
IExtension extension = extensions[i];
IConfigurationElement[] elements = extension.getConfigurationElements();
//获得扩展点配置元素
configurationElement[j].createExecutableExtension(“Class”);
//为每个回调对象创建实例
……
}
eclipse就是注册每一个extension和extension point,然后用extension point来实例化它对应的那个extension。
发生事件并向其它对象请求处理的对象被称为“事件对象”,而处理事件的对象被称为“回调对象”。回调对象由扩展者插件定义,由宿主插件创建实例。
Eclipse各bundle使用各自的class loader,若需要引用其他bundle类来动态创建实例,则需要使用类所在的bundle的class loader。