双击eclipse安装目录下的eclipse.exe运行后,会加载同一目录下的eclipse.ini文件(对应RCP项目也是一样,在RCP的安装目录下会有一个RCPName.ini文件):
-startup
plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.100.v20110502
-product
org.eclipse.epp.package.rcp.product
--launcher.defaultAction
openFile
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256M
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx512m
这里要说一下,
Eclipse的JVM启动的时候找JRE的顺序是:
如果eclipse.ini中配置了-vm参数,那么则使用这个参数指定的JRE(jvm.dll库路径);
否则,查看eclipse安装目录下是否有JRE文件夹,如果有的话就使用这个JRE;
否则,去系统注册表中查找安装的JRE,如果还找不到就报错。
所以如果不想卸载掉其他的JDK的话,可以有两种方式:
直接把要使用的JRE文件夹拷贝到Eclipse目录下
修改eclipse.ini文件,添加-vm参数,指定要运行的虚拟机的地址,形如:
-vm
C:\Program Files\Java\jdk1.6.0_12\bin\..\jre\bin\client\jvm.dll
(在eclipse.ini文件中添加配置项时,有空格就换行,不然会当成无效参数)
eclipse.ini文件中:
-startup 指定启动的入口为:安装目录下plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar
会加载该jar包中的org.eclipse.equinox.launcher.Main.class类并调用其main(String)完成app的启动过程
通过一个Exception.printStackTrace()方法来看一下Eclipse的大概启动过程:
图中是打印的状态栈,从下往上就是整个Eclipse(或者说RCP程序)的加载和启动过程,一直到App的启动。
下面来通过源代码,具体说明:
从org.eclipse.equinox.launcher.Main这个类开始:
在Main这个类的main()方法中下一个断的,调试状态启动Eclipse中的RCP程序就可以跟踪这个RCP启动的过程(Eclipse的过程也是类似的,其实EclipseIDE就是一个巨型的RCP):
public static void main(String[] args) {
int result = 0;
...
result = new Main().run(args);
...
}
然后看一下run()方法:
public int run(String[] args) {
...
basicRun(args);
...
}
主要实现就在basicRun()方法中:
protected void basicRun(String[] args) throws Exception {
//记录启动启动时间
System.getProperties().put("eclipse.startTime", Long.toString(System.currentTimeMillis())); //$NON-NLS-1$
commands = args;
//处理Command参数,并根据Command参数设置默认属性
String[] passThruArgs = processCommandLine(args);
if (!debug)
// debug can be specified as system property as well
debug = System.getProperty(PROP_DEBUG) != null;
setupVMProperties();//将VM参数写入到System.Properties中
processConfiguration();//加载配置信息
getInstallLocation();//获取安装路径,这里调用一下是为了确保InstallLocation被初始化
// locate boot plugin (may return -dev mode variations)
URL[] bootPath = getBootPath(bootLocation);//获取启动路径列表
setupJNI(bootPath);//启动JNIBridge,加载dll类库
//检查JDK版本
if (!checkVersion(System.getProperty("java.version"),
System.getProperty(PROP_REQUIRED_JAVA_VERSION)))
return;
//检查配置信息
if (!checkConfigurationLocation(configurationLocation))
return;
setSecurityPolicy(bootPath);//设置安全策略
handleSplash(bootPath);//启动闪屏,就是Eclipse(或RCP启动时IDE打开前带有进度条的界面)
beforeFwkInvocation();
invokeFramework(passThruArgs, bootPath);//加载框架(前面的工作都是辅助,这个才是加载框架的核心)
}
下面针对其中的几个重要方法进行说明:
processConfiguration:处理配置信息
private void processConfiguration() {
URL baseConfigurationLocation = null;
Properties baseConfiguration = null;
//在系统的配置文件中,键值对形如:
//osgi.configuration.area=file:/E:/eclipse-rcp-indigo-SR2-win32/eclipse/configuration/
if (System.getProperty(PROP_CONFIG_AREA) == null) {
String baseLocation = System.getProperty(PROP_BASE_CONFIG_AREA);
if (baseLocation != null)
baseConfigurationLocation = buildURL(baseLocation, true);
if (baseConfigurationLocation == null)
try {
//在并没指定参数的情况下,将会把Location指定到: 安装目录/configuration
baseConfigurationLocation = new URL(getInstallLocation(), CONFIG_DIR);
} catch (MalformedURLException e) {
// leave baseConfigurationLocation null
}
//加载目录下的config.ini文件,对其文件中的键值对 配置信息
baseConfiguration = loadConfiguration(baseConfigurationLocation);
... ...
}
Properties configuration = baseConfiguration;
if (configuration == null || !getConfigurationLocation().equals(baseConfigurationLocation))
configuration = loadConfiguration(getConfigurationLocation());
//把配置信息合并到System.getProperties()中
mergeProperties(System.getProperties(), configuration, null);
... ...
config.ini文件内容(举例)
#This configuration file was written by: org.eclipse.equinox.internal.frameworkadmin.equinox.EquinoxFwConfigFileParser
#Sun Feb 17 13:24:53 CST 2013
org.eclipse.update.reconcile=false
eclipse.p2.profile=epp.package.rcp
[email protected]/workspace
osgi.framework=file\:plugins/org.eclipse.osgi_3.7.2.v20120110-1415.jar
equinox.use.ds=true
eclipse.buildId=M20120208-0800
osgi.bundles=reference\:file\:org.eclipse.equinox.simpleconfigurator_1.0.200.v20110815-1438.jar@1\:start
org.eclipse.equinox.simpleconfigurator.configUrl=file\:org.eclipse.equinox.simpleconfigurator/bundles.info
eclipse.product=org.eclipse.platform.ide
osgi.splashPath=platform\:/base/plugins/org.eclipse.platform
osgi.framework.extensions=
osgi.bundles.defaultStartLevel=4
eclipse.application=org.eclipse.ui.ide.workbench
[email protected]/../p2/
getInstallLocation() 获取当前安装路径
private URL getInstallLocation() {
if (installLocation != null)
return installLocation;
//从系统配置信息中获取安装路径,有的话就直接返回
String installArea = System.getProperty(PROP_INSTALL_AREA);
if (installArea != null) {
installLocation = buildURL(installArea, true);
System.getProperties().put(PROP_INSTALL_AREA, installLocation.toExternalForm());
return installLocation;
}
//如果没有,则通过获取main类包的路径换算出安装路径
ProtectionDomain domain = Main.class.getProtectionDomain();
CodeSource source = null;
URL result = null;
source = domain.getCodeSource();
result = source.getLocation();
String path = decode(result.getFile());
... ...
installLocation = new URL(result.getProtocol(), result.getHost(), result.getPort(), path);
return installLocation;
}
getBootPath() 获取启动路径列表
protected URL[] getBootPath(String base) throws IOException {
URL url = null;
if (base != null) {
url = buildURL(base, true);
} else {
// search in the root location
url = getInstallLocation();
String path = new File(url.getFile(), "plugins").toString();
path = searchFor(framework, path);
if (url.getProtocol().equals("file"))
url = new File(path).toURL();
else
url = new URL(url.getProtocol(), url.getHost(), url.getPort(), path);
}
//键值对,形如:osgi.framework=file:/e:/eclipse/plugins/org.eclipse.osgi_3.7.2.v20120110-1415.jar
//指定osgi jar的路径
if (System.getProperty(PROP_FRAMEWORK) == null)
System.getProperties().put(PROP_FRAMEWORK, url.toExternalForm());
//获取启动路径列表
URL[] result = getDevPath(url);
return result;
}
setupJNI()启动JNIBridge,加载dll类库
private void setupJNI(URL[] defaultPath) {
String libPath = null;
/**
* 获取--launcher.library的路径, 形如:
* --launcher.library
* plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.100.v20110502
*/
if (library != null) {
File lib = new File(library);
if (lib.isDirectory()) {
libPath = searchFor("eclipse", lib.getAbsolutePath()); //$NON-NLS-1$
} else if (lib.exists()) {
libPath = lib.getAbsolutePath();
}
}
if (libPath == null) {
/**
* 根据OS、WS、Arch等信息,加载相应的本地文件(如,dll或so)。
*/
//find our fragment name
String fragmentOS = getOS();
String fragmentWS = getWS();
String fragmentArch = getArch();
libPath = getLibraryPath(getFragmentString(fragmentOS, fragmentWS, fragmentArch), defaultPath);
if (libPath == null && ws == null) {
// no ws was specified and we didn't find the default fragment, try an alternate ws
String alternateWS = getAlternateWS(fragmentWS);
libPath = getLibraryPath(getFragmentString(fragmentOS, alternateWS, fragmentArch), defaultPath);
if (libPath != null) {
System.getProperties().put(PROP_WS, alternateWS);
}
}
}
library = libPath;
if (library != null)
bridge = new JNIBridge(library);//并创建JNIBridge,这个还有待研究
}
invokeFramework()启动Equinox框架
private void invokeFramework(String[] passThruArgs, URL[] bootPath) throws Exception {
//如果没有指定ClassLoader,默认将boot设置为OSGi框架的ClassLoader的父类
String type = System.getProperty(PROP_FRAMEWORK_PARENT_CLASSLOADER,
System.getProperty(PROP_PARENT_CLASSLOADER, PARENT_CLASSLOADER_BOOT));
ClassLoader parent = null;
if (PARENT_CLASSLOADER_APP.equalsIgnoreCase(type))
parent = ClassLoader.getSystemClassLoader();
else if (PARENT_CLASSLOADER_EXT.equalsIgnoreCase(type)) {
ClassLoader appCL = ClassLoader.getSystemClassLoader();
if (appCL != null)
parent = appCL.getParent();
} else if (PARENT_CLASSLOADER_CURRENT.equalsIgnoreCase(type))
parent = this.getClass().getClassLoader();
//生成一个Equinox框架的StartupClassLoader,(关于ClassLoader分层机制,还有待研究)
URLClassLoader loader = new StartupClassLoader(bootPath, parent);
//通过该ClassLoader加载org.eclipse.core.runtime.adaptor.EclipseStarter类,并调用其run方法
Class clazz = loader.loadClass(STARTER);
Method method = clazz.getDeclaredMethod("run", new Class[] {String[].class, Runnable.class});
try {
//将命令行参数及闪屏线程对象传递给run方法
method.invoke(clazz, new Object[] {passThruArgs, splashHandler});
} catch (InvocationTargetException e) {
}
}
接下来看一下EclipseStarter.run()
public static Object run(String[] args, Runnable endSplashHandler) throws Exception {
if (Profile.PROFILE && Profile.STARTUP)
Profile.logEnter("EclipseStarter.run()", null); //$NON-NLS-1$
... ...
try {
startup(args, endSplashHandler);
... ...
Object obj=run(null);
return obj;
} catch (Throwable e) {
... ...
} finally {... ...}
return null;
}
public static BundleContext startup(String[] args, Runnable endSplashHandler) throws Exception {
... ...
FrameworkProperties.initializeProperties();
processCommandLine(args);
LocationManager.initializeLocations();
loadConfigurationInfo();
finalizeProperties();
if (Profile.PROFILE)
Profile.initProps(); // catch any Profile properties set in eclipse.properties...
adaptor = createAdaptor();//建立适配器
framework = new Framework(adaptor);//创建Equinox框架
context = framework.getBundle(0).getBundleContext();
registerFrameworkShutdownHandlers();
publishSplashScreen(endSplashHandler);//
consoleMgr = ConsoleManager.startConsole(framework);//控制台启动,会有信息输出
framework.launch();//启动框架
Bundle[] startBundles = loadBasicBundles();//loading basic bundles
... ...
// set the framework start level to the ultimate value. This will actually start things
// running if they are persistently active.
setStartLevel(getStartLevel());//StartLevel set
// they should all be active by this time
ensureBundlesActive(startBundles);
return context;
}
先到这里,有空继续分析
/**
* Runs the application for which the platform was started. The platform
* must be running.
* <p>
* The given argument is passed to the application being run. If it is <code>null</code>
* then the command line arguments used in starting the platform, and not consumed
* by the platform code, are passed to the application as a <code>String[]</code>.
* </p>
* @param argument the argument passed to the application. May be <code>null</code>
* @return the result of running the application
* @throws Exception if anything goes wrong
*/
public static Object run(Object argument) throws Exception {
if (Profile.PROFILE && Profile.STARTUP)
Profile.logEnter("EclipseStarter.run(Object)()", null); //$NON-NLS-1$
if (!running)
throw new IllegalStateException(EclipseAdaptorMsg.ECLIPSE_STARTUP_NOT_RUNNING);
// if we are just initializing, do not run the application just return.
if (initialize)
return new Integer(0);
try {
if (appLauncher == null) {
boolean launchDefault = Boolean.valueOf(FrameworkProperties.getProperty(PROP_APPLICATION_LAUNCHDEFAULT, "true")).booleanValue(); //$NON-NLS-1$
// create the ApplicationLauncher and register it as a service
appLauncher = new EclipseAppLauncher(context, Boolean.valueOf(FrameworkProperties.getProperty(PROP_ALLOW_APPRELAUNCH)).booleanValue(), launchDefault, log);
appLauncherRegistration = context.registerService(ApplicationLauncher.class.getName(), appLauncher, null);
// must start the launcher AFTER service restration because this method
// blocks and runs the application on the current thread. This method
// will return only after the application has stopped.
return appLauncher.start(argument);
}
return appLauncher.reStart(argument);
} catch (Exception e) {
if (log != null && context != null) // context can be null if OSGi failed to launch (bug 151413)
logUnresolvedBundles(context.getBundles());
throw e;
}
}