Dissect Eclipse Plugin Framework (6)
在介绍"Extension Point"之前,先来看一个概念:Eclipse中著名的懒加载原则(Lazy Loading Rule)。
懒加载法则:只有在真正需要的时候才加载插件,实现起来最重要的方面就是声明和实现的分离。
插件的外形(比如名字,ID,图标)等等都在插件描述清单"plugin.xml"中声明,而具体功能封装在class文件中。
这种懒加载原则表现在各个方面,比如最基本的插件启动。系统在启动的时候,只加载和启动最必须的一些插件,而其它插件只有在真正用到的时候才被加载和启动,这样可以最大限度的节省系统启动时的资源和时间。而对用户来说,每次启动也确实有很多插件根本不会去用到。
懒加载还表现在扩展点的应用上,待会儿可以看到具体例子。
接下来就看看"Extension Point",像前面曾经介绍的那样,"Extension Point"是Eclipse Plugin Frame中最核心的概念。首先来看一个Xerdoc DS中"Extension Point"和"Extension"的声明:
- <extension-point id="Parser">
- <parameter-def id="class" type="string"/>
- <parameter-def id="icon" type="string"/>
- </extension-point>
这是"core"插件中关于"Parser"的扩展点,你可以定义不同的扩展,来增强Xerdoc DS能够索引文件类型的范围。
- <extension plugin-id="com.xerdoc.desktop.core" point-id="Parser" id="MP3FileParser">
- <parameter id="class" value="com.xerdoc.desktop.parser.mp3.MP3FileParser"/>
- <parameter id="icon" value="image/mime_icon_Music_mp3.gif"/>
- </extension>
这是"mp3 parser"插件中对此扩展点的一个扩展声明,声明了自己扩展的类和图标。图标完全是为了显示,而其中的"class"则是为了加载真正的功能。
"core"插件会在需要的时候加载所有扩展了这个扩展点的插件:
- private static void loadSupportedParsers() {
- ... ...
- try {
- descriptor = manager.getPlugin("com.xerdoc.desktop.core")
- .getDescriptor();
- // 得到Parser扩展点声明
- IExtensionPoint extPoint = descriptor.getRegistry()
- .getExtensionPoint(descriptor.getId(), "Parser");
- // 根据这个声明得到所有连接到这个扩展点的扩展对象
- for (Iterator it = extPoint.getConnectedExtensions().iterator(); it
- .hasNext();) {
- IExtension ext = (IExtension) it.next();
- // 根据扩展对象生成Parser代理
- // 也就是著名的懒加载法则
- ParserProxy parser = ParserProxy.createParserProxy(ext);
- parserList_.add(parser);
- }
- } catch (PluginException e) {
- e.printStackTrace();
- }
- }
ParserProxy其实就是Parser的代理,它只读取Parser的表现部分,比如图标,名称等等,而实例化的操作要等到具体使用的时候才去调用。
- ...
- /**
- * Parser Extension Point
- */
- private IExtension extension_;
- /**
- * Real Parser Instance, it will not be load until really needed
- */
- private AbstractParser realParser_;
- ...
- private ParserProxy(IExtension extension) {
- extension_ = extension;
- }
- /**
- * Create the Parser Proxy based on the Extension
- *
- * @param extension
- * @return
- */
- public static ParserProxy createParserProxy(IExtension extension) {
- return new ParserProxy(extension);
- }
当真正需要这个Parser的时候,ParserProxy会生成相应的真实对象(如其名,这是Proxy模式的典型应用):
- private AbstractParser getRealInstance() {
- if (realParser_ == null) {
- try {
- // 得到插件
- // 如果插件还未被激活,这里要激活这个插件
- // LAZY LOADING!!!
- Plugin plugin = CorePlugin.getInstance().getManager()
- .getPlugin(
- extension_.getDeclaringPluginDescriptor()
- .getId());
- if (plugin != null) {
- Class pluginCls = plugin.getClass();
- // 得到主类
- Class cls = extension_.getDeclaringPluginDescriptor()
- .getPluginClassLoader().loadClass(
- extension_.getParameter("class")
- .valueAsString());
- if (cls != null) {
- if (pluginCls.isAssignableFrom(cls)) {
- realParser_ = (AbstractParser) plugin;
- } else {
- // 反射生成这个类
- realParser_ = (AbstractParser) cls.newInstance();
- }
- }
- }
- } catch (Exception e) {
- return null;
- }
- }
- return realParser_;
- }
然后,就可以调用这个Parser完成必要的工作了。这就是"Extension Point"的大概的工作流程。
在Eclipse中,遍地都是这样的例子,比如:Eclipse Platform的菜单显示就是一个扩展点,Eclipse在显示菜单之前首先会从系统的插件列表(PluginRegistry)中寻找所有扩展此扩展点的插件,取得图标和名字显示出来,然后在用户点击的时候生成真实的对象,并调用之,嗯,还是懒加载法则。
BTW:菜单扩展中的类是IAction,点击的时候调用它的run()函数,Command模式。
写这些的时候想起来,在声明每个扩展点前,这个插件都需要定义一定的Interface,也就是扩展这个扩展点的插件需要遵循的API。这个Interface如果用C#中的Delegate实现,是不是会看起来更好呢?(从包的import等等)。不知道有没有C#模仿Eclipse Plugin Framework的实例,呵呵。
Popularity: 27%
Related entries:

October 21st, 2005 at 2:01 am
C#下也有模仿Eclipse的Plugin Framework,是SharpDevelop。对它的分析可以参考偶的Blog,呵呵
October 21st, 2005 at 2:03 am
这个……很奇怪,一直无法确认你是否是CSDN的mengyan。虽然有照片…
不过我想他应该不会有你这么多的时间。不过你也说你读博士了……困惑ing
October 21st, 2005 at 7:05 am
呵呵,多谢你的信息。
BTW:我不是那个人 :P,我曾经是PhD Candidate,不过Quit了 … …