osgi类加载顺序源码解析

osgi容器中有三种classloader:各个bundle都有自己的classLoader,osgi容器也有一个框架级的classloader,这些classloader也不同于启动osgi容器的classloader(一般是app classloader)。如果需要加载一个类,那么就需要在这三个classloader之间挑出一个合适的classloader来加载类。本文将根据equinox和felix源码肤浅的描述一下osgi类加载顺序。

osgi规范中类加载顺序的定义

首先osgi类加载顺序不是开发osgi框架的人定的,而是osgi规范定的,但是每个框架的实现可能有所不同(规范详见osgi R4.3中的第3.9.4节 《Overall Search Order》):

在类和资源加载过程中,框架必须遵循以下规则。当请求一个bundle类加载器进行类加载或者资源的查找,查找必须按照以下顺序执行:

  1. 如果类或者资源是在包java.*中,那么交由父级类加载器代理完成,否则,搜索过程进入第二步。如果父类级类加载器加载失败,那么查找过程结束,加载失败。
  2. 如果类或者资源在启动代理序列(org.osgi.framework.bootdelegation)中定义,那么交由父级代理完成,此时的父级代理有启动参数org.osgi.framework.bundle.parent指定,默认是引导类加载器(bootstrap class loader),如果找到了类或者资源,那么查找过程结束。
  3. 如果类或者资源所在的包是在Import-Package中指定的,或者是在此之前通过动态导入加载的了,那么将请求转发到导出bundle的类加载器,否则搜索继续进行下一步;如果该包在启动参数org.osgi.framework.system.packages.extra中,则将请求转发给osgi容器外部的类加载器(通常是系统类加载器)。如果将请求交由导出类加载器代理,而类或者资源又没有找到,那么查找过程中止,同时请求失败。
  4. 如果包中类或者和资源所在的包由其他bundle通过是使用Require-Bundle从一个或多个其他bundle进行导入的了,那么请求交由其他那些bundle的类加载器完成,按照根据在bundle的manifest中指定的顺序进行查找进行查找。如果没有找到类或者资源,搜索继续进行。
  5. 使用bundle本身的内部bundle类路径查找完毕之后,。如果类或者资源还没有找到,搜索继续到下一步。
  6. 查找每一个附加的fragment的内部类路径,fragment的查找根据bundle ID顺序升序查找。如果没有找到类或者资源的,查找过程继续下一步。
  7. 如果包中类或者资源所在的包由bundle导出,或者包由bundle导入(使用Import-Package或者Require-Bundle),查找结束,即类或者资源没有找到。
  8. 否则,如果类或者资源所在的包是通过使用DynamicImport-Package进行导入,那么试图进行包的动态导入。导出者exporter必须符合包约束。如果找到了合适的导出者exporter,然后建立连接,以后的包导入就可以通过步骤三进行。如果连接建立失败,那么请求失败。
  9. 如果动态导入建立了,请求交由导出bundle的类加载器代理。如果代理查找失败,那么查找过程中止,请求失败。

下面的非标准流程图展示了查找的过程:

osgi类加载流程图
osgi类加载流程图

从规范可见,类加载的优先级顺序基本按照如下的原则:父容器classloader(通常是app classloader) –> 其他bundle的classloader –> 当前bundle的classloader –> 动态导入的包所在bundle的classloader。这个原则既可以使相同的类(包名也相同)尽可能只被加载一次,减少虚拟机perm区大小,也正因为如此,不同bundle中的相同的类,委托给同一个classloader加载,才能做到他们的对象和引用可以相互转换。(要知道一个类如果由不同的classloader加载后,其中一个classloader加载的类的对象是不能赋值给另一个classloader加载的类的引用的。)

osgi类加载顺序的源码解析

目前最流行的osgi框架实现有两个:equinoxfelix,下面分别列出equinox和felix在类加载顺序上的实现代码,如果大家在开发过程中发现一些类加载方面的奇怪现象,就可以通过找到如下的代码位置进行调试,从而跟踪出问题的根源,尤其是需要osgi容器内外共享类的时候。

equinox的osgi类加载顺序实现代码

在org.eclipse.osgi.internal.loader.BundleLoader.java的findClass(String name, boolean checkParent)和findClassInternal(String name, boolean checkParent, ClassLoader parentCL)方法中:

felix的osgi类加载顺序实现代码

在org.apache.felix.framework.BundleWiringImpl.java类的findClassOrResourceByDelegation(String name, boolean isClass) 方法中:

Reference

深入分析Java ClassLoader原理

 

如何让eclipse启动更快

通常eclipse会默认启动一些服务,其实提前启动这些的服务对我来说根本没有用,他们都是可以在需要使用的时候自动启动的,完全没必要提前启动,去掉这些服务对开发一点影响都没有,反而可以提升启动速度,节省一点内存。

在Window –> Preferences –> General –> Startup and Shutdown里面可以看到默认启动的服务有如下这些:

根据需要去掉eclipse startup设置中的服务可以大大提升eclipse启动速度
根据需要去掉eclipse startup设置中的服务可以大大提升eclipse启动速度
  • Marketplace Client:在需要安装插件的时候,会自动启动
  • Equinox Provisioning Platform Automatic Update Support:更新eclipse的,没发现这个有啥用
  • mylyn:用于任务管理,如果你没用到这个,就可以去掉
  • WindowBuilder Discovery UI:对用Java做windows gui开发的同学有用,如果你只做web开发,就果断去掉吧。
  • subclipse usage reporting:可以去掉,不影响subclipse的功能

根据自己的需要去掉一些服务,全部去掉也是ok的,然后重启eclipse感受下速度吧。

eclipse中文字体大小修改,让中英文字体协调

貌似有不少人苦恼eclipse中文字体大小修改问题,默认的eclipse中文字体很小,和英文字体大小完全不在一个调子上,因为默认的eclipse juno中英文字体是Consolas,字体大小是10,但是大家会发现,如果代码中有中文,那中文的字体会比英文的字体小很多,如下图:

默认的eclipse juno的中英文字体效果——eclipse中文字体大小修改
明显的,默认的eclipse juno中英文字体比中文字体大很多,显得中文很模糊——eclipse中文字体大小修改

如果大家把字体调大到中文字体看起来正常的话,那么英文字体就太大了,显得很不协调。网上也有一些方案,但是我觉得明显过于复杂,其实有很简单的办法,一步就可以搞定的。步骤如下:Window –> Preferences –> General –> Appearance –> Colors and Fonts,在“Colors and Fonts”中选择“Basic”–>”Text Font”,然后点“Edit”,此时只需要把“大小”里面的值编辑成“小五”即可。当你填入“小五”后,你会看到“大小”那一栏的下拉框中都变成了宋体的字体大小了,当然你也可以选择“五号”、“小四”、“四号”等等其他更大的字体。如下图:

把eclipse的字体大小设置成“小五”就可以把中文字体调大了
把eclipse的字体大小设置成“小五”就可以把中文字体调大了

然后点“确定”,再点“Apply”,“OK”,你就可以看到eclipse中文字体变大了,英文字体和大小不变(因为宋体的小五的英文字体大小正好和英文的10是相等的)。你可以通过把大小设置成宋体大小的更大值来同时调整中文英文的大小,这种设置方法可以让中英文字体协调美观。调整后的效果如下图:

eclipse的字体大小修改成小五后中英文字体明显协调多了
eclipse的字体大小修改成小五后中英文字体明显协调多了

相信这个方法应该比网上的其他修改eclipse中文字体的方法来得更直接更简单吧。