天天看點

Equinox加載Bundle Class的實作

equinox在建立bundle的classloader時,首先擷取bundle的classpath,然後執行createbclprevileged方法,此方法最後轉交由basedata來建立classloader。

  basedate建立classloader的關鍵代碼片段為:

classloadinghook[] hooks = adaptor.gethookregistry().getclassloadinghooks(); 

    classloader parent = adaptor.getbundleclassloaderparent(); 

    baseclassloader cl = null; 

    for (int i = 0; i < hooks.length && cl == null; i++) 

       cl = hooks[i].createclassloader(parent, delegate, domain, this, bundleclasspath); 

    if (cl == null) 

       cl = new defaultclassloader(parent, delegate, domain, this, bundleclasspath); 

    return cl;

  在equinox中,預設的情況下adaptor.getbundleclassloaderparent傳回的為bootstrap classloader,可通過修改啟動的osgi.parentclassloader 來改變這個parent classloader,

  osgi.parentclassloader 的可選值有四個,分别是:

  ● boot:預設

  ● app:systemclassloader

  ● ext:systemclassloader的parent

  ● fwk:啟動equinox的classloader

  classloadinghook在createclassloader的時候都沒有做動作,是以最後classloader都是通過建立defaultclassloader對象來建構的,其中parent參數為null,delegate參數為bundleloader執行個體,bundleclasspath參數為bundle的classpath。

  經過以上步驟後,完成了classloader的建立,可以開始加載class了,根據上面上述,bundle的class就由defaultclassloader來完成了。

  檢視defaultclassloader的loadclass代碼,發現真正的加載class的過程是轉為調用了delegate 的findclass來完成的,delegate參數對應的為bundleloader執行個體,轉為跟蹤bundleloader的findclass方法。

  bundleloader的findclass方法的代碼片段:

if (checkparent && parentcl != null && name.startswith(java_package)) 

   return parentcl.loadclass(name);

  如果不是java.開頭的類,則交由findclassinternal方法來完成加載。

  findclassinternal方法遵循的為osgi規範中定義的class的加載順序,不過仍然稍有改動:

  1)判斷是否交由parent classloader去完成加載

  在啟動equinox時,equinox會讀取org.osgi.framework.bootdelegation屬性,該屬性對應配置的為需要從parent classloader中加載的package,如值配置的為*,說明所有的都從parent classloader中加載 ,如值配置的為具體的package,那麼則放入bootdelegation集合;如配置的為帶通配符的package,那麼則放入bootdelegationstems集合。

  判斷時equinox首先判斷是否所有的都從parent classloader中加載,如是則從parent classloader中加載;

  如需要加載的類的package位于bootdelegation或bootdelegationstems集合中,那麼同樣從parent classloader中加載。

  如不從parent classloader中加載,則進入下面的步驟。

 2)嘗試調用equinox提供的classloaderdelegatehook的擴充來加載

  equinox對外提供了classloaderdelegatehook的接口擴充,可編寫classloaderdelegatehook的實作,注冊到framework中,那麼當有class需要加載等動作時都會得到通知。

  在預設情況下,equinox中沒有classloaderdelegatehook的實作,是以繼續下面的步驟。

  3)判斷是否在import-package中,如在則交由相應的packagesource去加載

  根據bundle配置的import-package,判斷目前需要加載的類是否在import-package中,如在則交由對應的packagesource進行加載,packagesource在加載時即直接交由對應的bundle的classloader去加載,如加載的類的package在import-package中,但加載後仍然沒有找到class,則直接抛出classnotfoundexception,如加載到,則直接傳回。

  如所需要加載的類的package不在import-package中,則繼續下面的步驟。

  4)嘗試從require-bundle中加載

  嘗試使用require-bundle來加載,如加載到,則直接傳回,如加載不到,則繼續下面的步驟。

  5)嘗試從目前bundle中加載

  直到經過以上步驟的嘗試,才嘗試由目前bundle中加載,目前bundle加載的方法即從bundle-classpath或目前bundle的fragment中查找相應名稱的class檔案,并讀取該檔案進行加載,如class檔案已加載,則進行緩存,再次加載時則不需要查找和解析class檔案。

  如從目前bundle中仍然未找到所需的類,則繼續下面的步驟。

  6)嘗試從dynamicimport-package中加載

  判斷需要找的類的package是否在dynamicimport-package中,如果在,則交由相應的packagesource進行加載,如packagesource中加載不到,則抛出classnotfoundexception;如不在dynamicimport-package中,則繼續下面的步驟。

  7)再次嘗試調用equinox提供的classloaderdelegatehook的擴充來加載

  這步和第2)步相同,是以在預設情況下繼續下面的步驟。

  8)嘗試使用eclipse的buddy機制來加載

  buddy機制是eclipse的擴充,并不符合osgi規範,是以在此不做深入分析。

  9)判斷一定的條件,如符合則從parent classloader中加載

  判斷的條件為:parent classloader不為null、不從parent classloader中加載、equinox的向後相容屬性(osgi.compatibility.bootdelegation)為true以及jvm的bug class,如滿足以上條件,則嘗試從parent classloader中加載。

  如經過以上所有步驟後,仍然未找到需要加載的class,則抛出classnotfoundexception。

  從上面的代碼分析中,在equinox中可以通過osgi.parentclassloader、org.osgi.framework.bootdelegation來控制從bundle classloader外來加載class,這對于內建equinox其他容器而言,非常有用,另外,還可以通過實作classloaderdelegatehook來改變class的加載。

Equinox加載Bundle Class的實作

本文出自seven的測試人生公衆号最新内容請見作者的github頁:http://qaseven.github.io/

繼續閱讀