Eclipse插件LazyStart实现原理分析

每次提到有关Eclipse插件启动的问题的时候,脑子中自然的反应就是:可以设定为预先启动 (org.eclipse.ui.startup),否则默认的情况下是懒启动(Lazy Start),只有当插件中的功能被真正 调用的时候,插件才会被启动。可能是人也跟着变懒了,也一直没有去留心Eclipse底层是怎么实现这种 懒加载的,只是有个大致的猜测,估计又是用hook机制了。昨天闲着具体看了一下实现,果然是类似的实 现。下面就大致和大家分享一下,说的不一定准确,仅供参考 ~_~。

直接进入主题,我们的Eclipse实例启动肯定要构造工作区,那么ResourcesPlugin肯定会被启动,我 们就在ResourcesPlugin.startup方法设置一个断点,调试栈如下:

假设我们对插件类型加载细节不知道,猜测大致过程如下:

1、DefaultClassLoader加载类型(org.eclipse.core.resources.IContainer)

2、EclipseLazyStarter.preFindLocalClass

3、启动资源插件:ResourcesPlugin.startup

补充说明:

1、org.eclipse.osgi.internal.baseadapTor.DefaultClassLoader是Eclipse针对OSGI类加载实现的 核心角色,也是eclipse插件默认的类加载器类型,当然,每个插件有自己独立的类加载器实例来负责类 型加载。

2、DefaultClassLoader、BundleLoader、ClasspathManager三者协作,处理类型加载请求(为什么一 个类加载过程要搞的这么复杂呢?Eclipse的考虑是什么呢? 大家思考吧~_~)

【EclipseLazyStarter调用分析】

我们先大致看一下EclipseLazyStarter.preFindLocalClass方法的代码实现:

1 public class EclipseLazyStarter implements ClassLoadingStatsHook, HookConfiguraTor {2   public void preFindLocalClass(String name, ClasspathManager manager) throws ClassNotFoundException {3     //首先判断,如果不需要启动则返回4     5     //如果插件正在启动,则设定5000ms超时等待;如果超时,直接报错返回67     //启动插件8   }9 }

加载类型之前为什么要给回调一下EclipseLazyStarter. preFindLocalClass,又hook了?我们看了一 下EclipseLazyStarter继承了ClassLoadingStatsHook接口,ClassLoadingStatsHook接口的类型API文档 说明了它的作用:

A ClassLoadingStatsHook hooks into the ClasspathManager class.

追踪前面的调用栈,ClassLoadingStatsHook是在ClasspathManager.findLocalClass中被调用的:

1 public Class findLocalClass(String classname) throws ClassNotFoundException {2     Class result = null;3     ClassLoadingStatsHook[] hooks = data.getAdapTor().getHookRegistry ().getClassLoadingStatsHooks();4     try {5       for (int i = 0; i < hooks.length; i++)6         hooks[i].preFindLocalClass(classname, this);7       result = findLocalClassImpl(classname, hooks);8       return result;9     } finally {10       for (int i = 0; i < hooks.length; i++)11         hooks[i].postFindLocalClass(classname, result, this);12     }13   }

再接着往下看之前,我们大致已经知道来的Eclipse的插件lazy start是怎么回事了:

EclipseLazyStarter hook到了插件类加载器的类型加载过程中了,在类型被加载之前会回调 EclipseLazyStarter. preFindLocalClass方法:如果类型所在插件还没启动,启动它;如果正在启动, 则设置5000ms的超时,限时不能完成启动,则报错返回!

(附加说明:头一段时间在另外一篇随笔中,写了一些编写插件启动类应该注意的点,其中有一条就 是避免在插件启动方法中干耗时的事情。这里真正告诉我们了原因:如果超过5000ms不能完成启动--注 意这其中还不包含所依赖插件的启动时间,那么肯定会出现类加载超时的错误了:

While loading class “{1}”, thread “{0}” timed out waiting ({4}ms) for thread “{2}” to finish starting bundle “{3}”. To avoid deadlock, thread “{0}” is proceeding but “{1}” may not be fully initialized.

【EclipseLazyStarter是如何完成注册过程的?】

过程简要解释如下:

1、启动osgi framework,两种启动方式:如果想利用Eclipse的一些特性,则就以EclipseStarter为 入口点启动;否则,可以用命令行的方式,以Laucher.main为入口点启动

2、初始化FrameworkAdapTor(对应eclipse实现是BaseAdapTor)看一下接口说明:

/*** FrameworkAdapTor interface to the osgi framework. This class is used to provide* platform. specific support for the osgi framework.**

The OSGi framework will call this class to perform. platform. specific functions.** Classes that implement FrameworkAdapTor MUST provide a construcTor that takes as a* parameter an array of Strings. This array will contain arguments to be* handled by the FrameworkAdapTor. The FrameworkAdapTor implementation may define the format* and content of its arguments.** The construcTor should parse the arguments passed to it and remember them.* The initialize method should perform. the actual processing of the adapTor* arguments.*

* Clients may implement this interface.*

* @since 3.1*/

显而易见,FrameworkAdapTor其实是osgi framework的后门,提供平台附加支持。

看一下BaseAdapTor的构造函数:

1 /**2   * Constructs a BaseAdapTor.3   * @param args arguments passed to the adapTor by the framework.4   */5   public BaseAdapTor(String[] args) {6     if (LocationManager.getConfigurationLocation() == null)7       LocationManager.initializeLocations();8     hookRegistry = new HookRegistry(this);9     FrameworkLogEntry[] errors = hookRegistry.initialize();10     if (errors.length > 0)11       for (int i = 0; i < errors.length; i++)12         getFrameworkLog().log(errors[i]);13     // get the sTorage after the registry has been initialized14     sTorage = getSTorage();15     // TODO consider passing args to BaseAdapTorHooks16   } 

我们看到,调用了HookRegistry.initialize进行初始化

3、初始化HookRegistry,我们直接看一下HookRegistry.initialize方法实现

1 /**2   * Initializes the hook configuraTors. The following steps are used to initialize the hook configuraTors.

3   * 1. Get a list of hook configuraTors from all hook configuraTors properties files on the classpath,4   *  add this list to the overall list of hook configuraTors, remove duplicates.

5   * 2. Get a list of hook configuraTors from the ("osgi.hook.configuraTors.include") system property6   *  and add this list to the overall list of hook configuraTors, remove duplicates.

7   * 3. Get a list of hook configuraTors from the ("osgi.hook.configuraTors.exclude") system property8   *  and remove this list from the overall list of hook configuraTors.

9   * 4. Load each hook configuraTor class, create a new instance, then call the {@link HookConfiguraTor#addHooks(HookRegistry)} method

10   * 5. Set this HookRegistry object to read only to prevent any other hooks from being added.

11   * @return an array of error log entries that occurred while initializing the hooks12   */13   public FrameworkLogEntry[] initialize() {14     ArrayList configuraTors = new ArrayList(5);15     ArrayList errors = new ArrayList(0); // optimistic that no errors will occur16     mergeFileHookConfiguraTors(configuraTors, errors);17     mergePropertyHookConfiguraTors(configuraTors);18     loadConfiguraTors(configuraTors, errors);19     // set to read-only20     readonly = true;21     return (FrameworkLogEntry[]) errors.toArray(new FrameworkLogEntry[errors.size ()]);22   }

其中的mergeFileHookConfiguraTors方法调用,读取了一个名为hookconfiguraTors.properties的属 性配置文件,在org.eclipse.osgi插件中。看一下里面的内容:

1 ###############################################################################2 # Copyright (c) 2005, 2006 IBM Corporation and others.3 # All rights reserved. This program and the accompanying materials4 # are made available under the terms of the Eclipse Public License v1.05 # which accompanies this distribution, and is available at6 # http://www.eclipse.org/legal/epl-v10.html7 #8 # ContribuTors:9 # IBM Corporation - initial API and implementation10 ###############################################################################11 hook.configuraTors= /12 org.eclipse.osgi.internal.baseadapTor.BaseHookConfiguraTor,/13 org.eclipse.osgi.internal.baseadapTor.DevClassLoadingHook,/14 org.eclipse.core.runtime.internal.adapTor.EclipseSTorageHook,/15 org.eclipse.core.runtime.internal.adapTor.EclipseLogHook,/16 org.eclipse.core.runtime.internal.adapTor.EclipseErrorHandler,/17 org.eclipse.core.runtime.internal.adapTor.EclipseAdapTorHook,/18 org.eclipse.core.runtime.internal.adapTor.EclipseClassLoadingHook,/19 org.eclipse.core.runtime.internal.adapTor.EclipseLazyStarter,/20 org.eclipse.core.runtime.internal.stats.StatsManager,/21 org.eclipse.osgi.internal.verifier.SignedBundleHook22

^_^,我们的EclipseLazyStarter赫然在列!!!

回过头来看一下EclipseLazyStarter(继承ClassLoadingStatsHook)的使用方式:

BaseAdapTor.getHookRegistry().getClassLoadingStatsHooks()

前面已经看了ClasspathManager中findLocalClass方法的代码,就是这么调用ClassLoadingStatsHook policy的(我们的EclipseLazyStarter…)

【总结】

hook了,osgi framework留了个后门,Eclipse好好的利用了这个后门~_~

【附加说明】

1、EclipseLazyStarter只是ClassLoadingStatsHook policy的实现,其实HookRegsitry中还有其他的 hook policy,例如:

org.eclipse.osgi.baseadapTor.hooks.ClassLoadingHook   org.eclipse.osgi.baseadapTor.hooks.BundleFileWrapperFacToryHook   org.eclipse.osgi.baseadapTor.hooks.BundleFileFacToryHook   org.eclipse.osgi.baseadapTor.hooks.STorageHook   org.eclipse.osgi.baseadapTor.hooks.AdapTorHook

2、大家可以顺带详细的看一下HookRegistry、HookConfiguraTor、BaseAdapTor等

3、hook这种手法在Eclipse的资源管理中也有比较成功的应用,可以看一下

org.eclipse.core.resources.team.IMoveDeleteHook

例如cvs、ClearCase等团队开发管理工具中,都实现了这种hook,通过扩展点 org.eclipse.core.resources.moveDeleteHook动态挂入。大家有兴趣可以深入看看,看过之后应该就明 白了为什么cvs、ClearCase等一些团队开发管理工具功能有一些不同了~_~

4、对osgi感兴趣的同学,可以看一下org.eclipse.osgi插件中的代码,质量很高~_~

乱轰轰的,凑合着看吧

三亚呀——赴一个蓝天碧海。

Eclipse插件LazyStart实现原理分析

相关文章:

你感兴趣的文章:

标签云: