在Eclipse RCP应用中利用扩展点机制解藕插件的依赖关系

来源:互联网 发布:eclipse4.7 linux 编辑:程序博客网 时间:2024/04/29 13:55

Eclipse RCP应用中利用扩展点机制解藕插件的依赖关系

 

在开发Eclipse RCP应用程序时,我们按照Eclipse插件的思路来组织和划分我们的程序模块,会使系统结构得到很大改善:比如,系统功能的灵活装配,系统的增量开发等。为了达到这种效果,我们要尽量减少插件之间的依赖关系。Eclipse平台中提供的扩展点机制可以用来实现这种目标。

 

一、业务场景

 

下面以一个具体的业务场景来说明:

比如,在我们的某个应用中,有WorkspaceDocumentJob三种业务功能,我们用三个Eclipse插件工程(WorkspacePluginDocumentPluginJobPlugin)分别来实现这些功能。

在三个插件中,WorkspaceView管理所有的Workspace业务对象,DocumentView管理所有的Document业务对象,JobView管理所有的Job业务对象。

同时,这三种业务不是孤立的,它们之间是有交互的:打开WorkspaceView中的某个WorkspaceDocumentView会作相应改变:显示该Workspace下的所有Document。同样,JobView也会根据Workspace的变化作相应的改变。

 

针对以上的业务场景,一种自然的做法是,在WorkspacePlugin中添加对另外两个插件的引用,直接在WorkspaceView中调用DocumentViewJobView的相关方法。

但是这种做法导致了三个插件之间比较强的依赖关系,和我们的系统功能的灵活装配的目标有所差距。下面我们利用Eclipse扩展点机制来解决这种问题。

 

二、定义扩展点

 

不难看出,上述场景是一个典型的Observer模式的应用:WorkspaceView是交互的发起者,而DocumentViewJobView是交互的监听者。

为了简便起见,在这里不再创建新的插件来容纳我们的接口和扩展点,我们在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.exsdXML文件,同时会打开扩展点模式编辑器。

 

在扩展点模式编辑器中,我们输入一些该扩展点的其他信息,然后切换到“定义”Tab页:

新建一个名称为“listener”的元素,然后在“listener”下新建一个名为“class”的属性节点。

选中“class”节点,在右边的面板上编辑它的详细信息,在第三栏“使用”,默认是“optional”,我们这里改为“required”。这表明其他插件在使用本扩展点时,“class”属性必须指定。

然后,我们在默认的“extension”元素上,新建一个“序列”(sequence)。在“序列”点击右键,新建“引用”,将上面建立的“listener”元素加入序列。

 

至此,一个简单的扩展点定义完毕。

下图是在扩展点模式编辑器中看到的扩展点的定义结构的截图:

 

三、使用扩展点

 

上面的扩展点定义完毕,WorkspacePlugin更新后,我们分别在DocumentPluginJobPlugin的插件依赖项中加上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扩展点,在DocumentPluginJobPlugin中使用了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 中文版》