在
Eclipse RCP
应用中利用扩展点机制解藕插件的依赖关系
在开发
Eclipse RCP
应用程序时,我们按照
Eclipse
插件的思路来组织和划分我们的程序模块,会使系统结构得到很大改善:比如
,
系统功能的灵活装配,系统的增量开发等。为了达到这种效果,我们要尽量减少插件之间的依赖关系。
Eclipse
平台中提供的扩展点机制可以用来实现这种目标。
一、业务场景
下面以一个具体的业务场景来说明:
比如,在我们的某个应用中,有
Workspace
、
Document
、
Job
三种业务功能,我们用三个
Eclipse
插件工程(
WorkspacePlugin
、
DocumentPlugin
、
JobPlugin
)分别来实现这些功能。
在三个插件中,
WorkspaceView
管理所有的
Workspace
业务对象,
DocumentView
管理所有的
Document
业务对象,
JobView
管理所有的
Job
业务对象。
同时,这三种业务不是孤立的,它们之间是有交互的:打开
WorkspaceView
中的某个
Workspace
,
DocumentView
会作相应改变:显示该
Workspace
下的所有
Document
。同样,
JobView
也会根据
Workspace
的变化作相应的改变。
针对以上的业务场景,一种自然的做法是,在
WorkspacePlugin
中添加对另外两个插件的引用,直接在
WorkspaceView
中调用
DocumentView
和
JobView
的相关方法。
但是这种做法导致了三个插件之间比较强的依赖关系,和我们的系统功能的灵活装配的目标有所差距。下面我们利用
Eclipse
扩展点机制来解决这种问题。
二、定义扩展点
不难看出,上述场景是一个典型的
Observer
模式的应用:
WorkspaceView
是交互的发起者,而
DocumentView
和
JobView
是交互的监听者。
为了简便起见,在这里不再创建新的插件来容纳我们的接口和扩展点,我们在
WorkspacePlugin
定义一个
Workspace
监听器接口:
IWorkspaceListener
public
interface
IWorkspaceListener {
void
workspaceOpened(Workspace ws);
void
workspaceClosed(Workspace ws);
}
接下来,我们针对
IWorkspaceListener
接口定义
Workspace
监听器扩展点:
workspacelisteners
。
//
注:我的开发环境是
Eclipse3.2+
中文语言包
,
如果是其他版本和语言,界面文字可能和下面的描述有所出入。
在
Eclipse IDE
中,打开
WorkspacePlugin
下的
plugin.xml
(用插件清单编辑器的方式打开),切换到“扩展点”
Tab
页,新建一个扩展点:
在“扩展点标识”一栏输入我们上面确定的扩展点名称:
workspacelisteners
,在“扩展点名称”一栏输入一个友好的用于显示的名称,比如:
workspace listeners
。确定后,将会在工程的
schema
目录下生成一个
workspacelisteners.exsd
的
XML
文件,同时会打开扩展点模式编辑器。
在扩展点模式编辑器中,我们输入一些该扩展点的其他信息,然后切换到“定义”
Tab
页:
新建一个名称为“
listener
”的元素,然后在“
listener
”下新建一个名为“
class
”的属性节点。
选中“
class
”节点,在右边的面板上编辑它的详细信息,在第三栏“使用”,默认是“
optional
”,我们这里改为“
required
”。这表明其他插件在使用本扩展点时,“
class
”属性必须指定。
然后,我们在默认的“
extension
”元素上,新建一个“序列”
(sequence)
。在“序列”点击右键,新建“引用”,将上面建立的“
listener
”元素加入序列。
至此,一个简单的扩展点定义完毕。
下图是在扩展点模式编辑器中看到的扩展点的定义结构的截图:
三、使用扩展点
上面的扩展点定义完毕,
WorkspacePlugin
更新后,我们分别在
DocumentPlugin
和
JobPlugin
的插件依赖项中加上
WorkspacePlugin
,这样在这两个插件中就可以使用
workspacelisteners
了。
使用
workspacelisteners
扩展点和使用
Eclipse RCP
中提供的其他扩展点没有任何区别。下面以
DocumentPlugin
为例简单说明。
根据业务场景的描述,
DocumentView
应该实现
IWorkspaceListener
接口:
public
class
DocumentView
extends
ViewPart
implements
IWorkspaceListener{
public
void
workspaceOpened(Workspace ws){
//todo...
//
实现
workspace
打开后的相关更新
}
void
workspaceClosed(Workspace ws){
//todo...
//
实现
workspace
关闭后的相关更新
}
}
接下来,我们在
plugin.xml
中声明使用
workspacelisteners
扩展点:
打开
plugin.xml
文件,切换到“扩展”
Tab
页,添加扩展点,找到
workspacelisteners
,确定。在“所有扩展”列表中就会看到“
workspacelisteners
”节点,右键点击“
workspacelisteners
”节点,新建一个扩展元素,选择我们前面定义的“
listener
”。
然后设置
listener
的“
class
”属性为
DocumentView
类的完整类名
(
包括
package)
,这里是“
document.DocumentView
”。
至此,
DocumentPlugin
中使用
workspacelisteners
的相关工作已经完毕。在
JobPlugin
使用和上述基本类似,这里不再赘述。
四、加载扩展点
在前面的描述中,我们在
WorkspacePlugin
中定义了
workspacelisteners
扩展点,在
DocumentPlugin
和
JobPlugin
中使用了
workspacelisteners
扩展。下面我们需要在
WorkspacePlugin
加载所有的扩展,以便
WorkspaceView
变化时向这些扩展发出通知。
我们在
WorkspaceView
定义一个方法来加载扩展。
public
class
WorkspaceView
extends
ViewPart {
private
List<IWorkspaceListener> listeners=
new
ArrayList<IWorkspaceListener>();
private
Workspace ws;
//
打开的
Workspace
public
void
loadWorkspaceListeners(){
IExtensionRegistry registry = Platform.getExtensionRegistry();
//
注:这里传入的扩展点的名称必须是全名,包括
plugin
的名称。
IExtensionPoint point = registry.getExtensionPoint(
"workspace.workspacelisteners"
);
IExtension[] extensions = point.getExtensions();
for
(
int
i=0;i<extensions.length;i++){
IConfigurationElement[] elements = extensions[i].getConfigurationElements();
for
(
int
j=0;j<elements.length;j++){
try
{
Object listener=elements[j].createExecutableExtension(
"class"
);
if
(listener
instanceof
IWorkspaceListener)
listeners.add((IWorkspaceListener)listener);
}
catch
(CoreException e){
//....todo
}
}
}
}
//
当界面操作打开
Workspace
时
,
调用此方法通知所有
IWorkspaceListener
public
void
notifyWorkspaceOpened(){
Iterator<IWorkspaceListener> it=listeners.iterator();
while
(it.hasNext())
it.next().workspaceOpened(ws);
}
//
当界面操作关闭
Workspace
时
,
调用此方法通知所有
IWorkspaceListener
public
void
notifyWorkspaceClosed(){
Iterator<IWorkspaceListener> it=listeners.iterator();
while
(it.hasNext())
it.next().workspaceClosed(ws);
}
}
参考资料:
《
Contributing to Eclipse
中文版》