Delegation Model
The class loaders are hierarchical in nature, they use delegation model when loading the class.
當classloader有類需要載入時先讓其parent搜尋其搜尋路徑幫忙載入,如果parent找不到,在由自己搜尋自己的搜尋路徑載入,如果自訂ClassLoader沒有改寫 loadClass method 的情況下,只有在一個 class 尚未被載入過,且parent class loader 也無法載入指定的 class,才會使用到 findClass method。
可參考:
Java Classloader Wiki
Understanding Java Classloaders
解讀ClassLoader
OSGi最常碰到的問題就是ClassLoader,OSGi中的每個Bundle 都有一個獨立的Class Loader,Bundle 之間通過不同的ClassLoader和在MANIFEST.MF 文件中定義的條件達到class的共享。
bundle之間類的共享:
通過export package的方式實現的,在bundle的manifest中通過制定export package的方式將特定的package與其他的bundle共享。
而引用其他bundle所暴露的package有兩種方式,第一是通過import package的方式,第二種是通過required bundle的方式
OSGi容器為每個Bundle建立不同的classloader,因此每個Bundle能訪問位於下列位置中的class:
a) 位於Java啟動classpath下的、所有以Java.*開頭的package class;
b) 位於OSGi框架classlpath下的class,通常有一個獨立的classloader負責加載框架的實現類及關鍵的interface class;
c) 位於Bundle空間中的class,這些類通常包含在與Bundle相關的jar文件中,以及加到這個Bundle中的其它jar包中的class。
d) import package中的class
實際運行環境中,Bundle 的Class Loader 根據如下規則去搜索類資源:
1:如Class Resource 屬於java.* 包,則將加載請求委託給父加載器
2:如Class Resource 定義在OSGi 框架中啟動委託列表(org.osgi.framework.bootdelegation)中,則將加載請求委託給父加載器
3:如Class Resource 屬於在Import-Package 中定義的包,則框架通過Class Loader 依賴關係圖找到導出此package的Bundle 的Class Loader,並將加載請求委託給此Class Loader
4:如Class Resource 屬於在Require-Bundle 中定義的Bundle,則框架通過Class Loader 依賴關係圖找到此5:Bundle 的Class Loader,將加載請求委託給此Class Loader ;
6:Bundle 搜索自己的類資源( 包括Bundle-Classpath 裡面定義的類路徑和屬於Bundle 的Fragment 的類資源)
7:若類在DynamicImport-Package 中定義,則開始嘗試在運行環境中尋找符合條件的Bundle 。
下圖是一個簡單的osgi classloader :
然後這個是一個完整的osgi classloader
建立一個Spring Dm bundle , 設定bean Start() method
<bean id="actavitor" class="com.my.loader.internal.Actavitor" init-method="start" destroy-method="stop"> </bean>
Actavitor.java
public void start() throws Exception { System.out.println("Hello Spring OSGi World!! "); System.out.println("class->"+this.getClass()); System.out.println("classLoader->"+this.getClass().getClassLoader()); System.out.println("classLoader.getParent->"+this.getClass().getClassLoader().getParent()); System.out.println("classLoader.getParent.getClass->"+this.getClass().getClassLoader().getParent().getClass()); System.out.println("applicationCtx.getClass ->"+applicationCtx.getClass()); System.out.println("applicationCtx.getClassLoader ->"+applicationCtx.getClassLoader()); System.out.println("applicationCtx.getClassLoader.getClass ->"+applicationCtx.getClassLoader().getClass()); }
Result
[2011-04-25 13:39:26.093] region-dm-11 System.out Hello Spring OSGi World!! [2011-04-25 13:39:26.093] region-dm-11 System.out class->class com.my.loader.internal.Actavitor [2011-04-25 13:39:26.093] region-dm-11 System.out classLoader->KernelBundleClassLoader: [bundle=com.my.loader.BundleClassLoaderTester_1.0.0] [2011-04-25 13:39:26.093] region-dm-11 System.out classLoader.getParent->org.eclipse.osgi.launch.EquinoxFWClassLoader@1d77d9e [2011-04-25 13:39:26.093] region-dm-11 System.out classLoader.getParent.getClass->class org.eclipse.osgi.launch.EquinoxFWClassLoader [2011-04-25 13:39:26.093] region-dm-11 System.out applicationCtx.getClass ->class org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext [2011-04-25 13:39:26.093] region-dm-11 System.out applicationCtx.getClassLoader ->BundleDelegatingClassLoader for [Spring OSGi Bundle (com.my.loader.BundleClassLoaderTester)]
Eclipse Virgo 的Bundle ClassLoader 為KernelBundleClassLoader
而KernelBundleClassLoader的parent為EquinoxFWClassLoader
Spring DM 會為每個bundle建立ApplicationContext , classloader透過BundleDelegatingClassLoader
BundleDelegatingClassLoader部份代碼
private final ClassLoader bridge; . . /** * Private constructor. * * Constructs a new <code>BundleDelegatingClassLoader</code> instance. * * @param bundle * @param bridgeLoader */ protected BundleDelegatingClassLoader(Bundle bundle, ClassLoader bridgeLoader) { super(null); Assert.notNull(bundle, "bundle should be non-null"); this.backingBundle = bundle; this.bridge = bridgeLoader; } protected Class findClass(String name) throws ClassNotFoundException { try { return this.backingBundle.loadClass(name); } catch (ClassNotFoundException cnfe) { DebugUtils.debugClassLoading(backingBundle, name, null); throw new ClassNotFoundException(name " not found from bundle [" backingBundle.getSymbolicName() "]", cnfe); } catch (NoClassDefFoundError ncdfe) { // This is almost always an error // This is caused by a dependent class failure, // so make sure we search for the right one. String cname = ncdfe.getMessage().replace('/', '.'); DebugUtils.debugClassLoading(backingBundle, cname, name); NoClassDefFoundError e = new NoClassDefFoundError(name " not found from bundle [" OsgiStringUtils.nullSafeNameAndSymName(backingBundle) "]"); e.initCause(ncdfe); throw e; } } . . . .
OSGi classLoader basic有提到 DefaultClassLoader. To get the allocated Bundle of a DefaultClassLoader we can call the getDelegate() Method.SpringDm 裡的BundleDelegatingClassLoader有設定bridge classloader, class loader adapter..主要還是透過org.osgi.framework.Bundle 的classloader來取的每個Bundle 的classloader。
Reference:
Basics about OSGi Classloading
http://codescale.wordpress.com/2009/05/22/basics-about-osgi-classloading/
Class Loader Architecture Comparison – Java, J2EE and OSGi
http://shylendrabhat.com/blog/2009/11/21/class-loader-architecture-comparison-java-j2ee-and-osgi/
classloader相關基礎知識
http://www.iteye.com/topic/25053
探索OSGi 框架的組件運行機制
http://www.ibm.com/developerworks/cn/java/j-lo-osgi/
深入了解Java ClassLoader、Bytecode 、ASM、cglib
http://www.iteye.com/topic/98178
http://blog.csdn.net/chief1985/archive/2009/08/12/4440398.aspx
沒有留言:
張貼留言