<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    豬兒笨笨的文檔

    主要是個(gè)人的一些思考和技術(shù)文章,還有許多翻譯的文檔

     

    Wicket1.3中Class熱加載--揭秘篇

     

    在文章《Wicket1.3中Class熱加載--使用篇》中,展示了如何使用Wicket1.3提供的ReloadingWicketFilter來(lái)動(dòng)態(tài)加載修改后的類(lèi)(包括修改了簽名的類(lèi)),從而實(shí)現(xiàn)高效開(kāi)發(fā)。但在該文章中,只是給出了如何使用該功能的說(shuō)明,而本篇文章將明確解析Wicket1.3類(lèi)熱加載魔法的奧秘所在。

    在上一篇文章中,是通過(guò)修改Wicket項(xiàng)目中web.xml文件,將其中的

    org.apache.wicket.protocol.http.WicketFilter

    全部替換成

    org.apache.wicket.protocol.http.ReloadingWicketFilter

    從而開(kāi)啟了Wicket類(lèi)熱加載的功能。那么為了探究其魔法奧秘,入手點(diǎn)自然就選擇ReloadingWicketFilter這個(gè)類(lèi)了。

    先來(lái)看一下ReloadingWicketFilter的代碼,驚人的少:

    public class ReloadingWicketFilter extends WicketFilter

    {

    private ReloadingClassLoader reloadingClassLoader;

    /**

     * Instantiate the reloading class loader

     */

    public ReloadingWicketFilter()

    {

    // Create a reloading classloader

    reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader());

    }

    /**

     * @see org.apache.wicket.protocol.http.WicketFilter#getClassLoader()

     */

    protected ClassLoader getClassLoader()

    {

    return reloadingClassLoader;

    }

    /**

     * @see org.apache.wicket.protocol.http.WicketFilter#init(javax.servlet.FilterConfig)

     */

    public void init(final FilterConfig filterConfig) throws ServletException

    {

    reloadingClassLoader.setListener(new IChangeListener()

    {

    public void onChange()

    {

    // Remove the ModificationWatcher from the current reloading class loader

    reloadingClassLoader.destroy();

    /*

     * Create a new classloader, as there is no way to clear a ClassLoader's cache. This

     * supposes that we don't share objects across application instances, this is almost

     * true, except for Wicket's Session object.

     */

    reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader());

    try

    {

    init(filterConfig);

    }

    catch (ServletException e)

    {

    Throw new RuntimeException(e);

    }

    }

    });

    super.init(filterConfig);

    }

    }

    其中最引人注目的就是那個(gè)ReloadingClassLoader,再打開(kāi)WicketFilter類(lèi)中,很容易找到以下代碼,它表示使用自定義的ClassLoader來(lái)加載類(lèi)。

    final ClassLoader newClassLoader = getClassLoader();

    Thread.currentThread().setContextClassLoader(newClassLoader);

    而ReloadingWicketFilter則是重載了getClassLoader方法,以返回了自定義的ReloadingClassLoader。也就是說(shuō)Wicket的魔法其實(shí)是使用了一個(gè)自定義的ReloadingClassLoader來(lái)實(shí)現(xiàn)類(lèi)的熱加載(包括對(duì)簽名被修改的類(lèi))。為了讓大家更清楚理解Wicket這一方法以,在分析ReloadingClassLoader之前,先來(lái)簡(jiǎn)單的過(guò)一下JVM的類(lèi)加載機(jī)制。

    在JDK1.2以后,JVM在加載類(lèi)時(shí)默認(rèn)采用的是雙親委機(jī)制(早期的類(lèi)加載機(jī)制存在安全漏洞)。通俗的講,就是某個(gè)特定的類(lèi)加載器在接到加載類(lèi)的請(qǐng)求時(shí),首先將加載任務(wù)委托給父類(lèi)加載器,依次遞歸,所有 ClassLoaders 的根是系統(tǒng) ClassLoader,它會(huì)以缺省方式裝入類(lèi)本地文件系統(tǒng)加載(可能是Jar包,也可能是Class文件),如果父類(lèi)加載器可以完成類(lèi)加載任務(wù),就成功返回加載后的類(lèi)但如果父類(lèi)加載器無(wú)法完成此加載任務(wù)時(shí),那么這個(gè)特定的類(lèi)加載才自己去加載指定名稱(chēng)的類(lèi)這樣的雙親委機(jī)制可以保證象java.io.*這種基礎(chǔ)類(lèi)庫(kù)的內(nèi)容一定是被系統(tǒng)ClassLoader加載,從而保證類(lèi)加載的安全性。

    下面是雙親委派機(jī)制的示意圖:

    實(shí)例分析Web應(yīng)用下的類(lèi)加載順序:

    為了更好的方便大家理解類(lèi)的加載機(jī)制,并說(shuō)明Wicket如何使用自定義的ClassLoader來(lái)加載更改后的類(lèi),下面將有一個(gè)簡(jiǎn)單的實(shí)例來(lái)說(shuō)明。

    首先將前一篇文章中的HelloWorld代碼修改為:

    public class HelloWorld extends WicketExamplePage

    {

    /**

     * Constructor

     */

    public HelloWorld()

    {

    ClassLoader classLoader = this.getClass().getClassLoader();

    while (null != classLoader) 

    {

    System.err.println("loader   " + classLoader.hashCode()+"   "+classLoader.getClass());

    classLoader = classLoader.getParent();

    }

    add(new Label("message""New Hello World!"));

    }

    }

    修改以后的代碼,可以在對(duì)象被創(chuàng)建時(shí),輸出HelloWorld類(lèi)的ClassLoader及其父ClassLoader,接下來(lái)恢復(fù)web.xml文件中的filter為WicketFilter,不使用RelodingWicketFilter,從而觀察原先的Class加載順序,得到的結(jié)果為:

    loader   2011334   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    接下來(lái)仍然按照上一篇文章中的操作修改web.xml,使用RelodingWicketFilter,開(kāi)啟Wicket類(lèi)的熱加載功能。再看一下輸出結(jié)果:

    loader   8310913   class org.apache.wicket.application.ReloadingClassLoader

    loader   2011334   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    可見(jiàn)通過(guò)代碼

    final ClassLoader newClassLoader = getClassLoader();

    Thread.currentThread().setContextClassLoader(newClassLoader);

    ReloadingWicketFilter使用ReloadingClassLoader作為當(dāng)前類(lèi)的ClassLoader,也就是說(shuō)它接管了所有WEB-INF/classes目錄下面類(lèi)文件的加載。這樣它就可以根據(jù)實(shí)際情況來(lái)加載一個(gè)類(lèi)。但有經(jīng)驗(yàn)的程序員都有知道,一般來(lái)說(shuō)Class一旦被加載,就表示在整個(gè)JVM生命周期的過(guò)程中,不會(huì)自動(dòng)釋放,而是放置在內(nèi)存中。那么Wicket又是怎么釋放已經(jīng)加載的類(lèi),同時(shí)加載修改后的類(lèi)呢?看一段ReloadingWicketFilter中init方法的代碼:

    /**

     * @see org.apache.wicket.protocol.http.WicketFilter#init(javax.servlet.FilterConfig)

     */

    public void init(final FilterConfig filterConfig) throws ServletException

    {

    reloadingClassLoader.setListener(new IChangeListener()

    {

    public void onChange()

    {

    // Remove the ModificationWatcher from the current reloading class loader

    reloadingClassLoader.destroy();

    /*

     * Create a new classloader, as there is no way to clear a ClassLoader's cache. This

     * supposes that we don't share objects across application instances, this is almost

     * true, except for Wicket's Session object.

     */

    reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader());

    try

    {

    init(filterConfig);

    }

    catch (ServletException e)

    {

    Throw new RuntimeException(e);

    }

    }

    });

    super.init(filterConfig);

    }

    這段代碼表示,一旦發(fā)現(xiàn)類(lèi)文件的改變,將會(huì)銷(xiāo)毀當(dāng)前的reloadingClassLoader ,同時(shí)新建一個(gè)ReloadingClassLoader的實(shí)例,因?yàn)镃lassLoader被銷(xiāo)毀了,所以由該ClassLoader加載的類(lèi)都將被銷(xiāo)毀,然后再由新的ReloadingClassLoader進(jìn)行加載,因此即使是修改了簽名的類(lèi)也是可以被正確加載成功的。當(dāng)然這種做法,可能會(huì)引起Session中的數(shù)據(jù)不能正確識(shí)別和轉(zhuǎn)換。但相對(duì)于開(kāi)發(fā)環(huán)境下,開(kāi)發(fā)人員通過(guò)另起一個(gè)新的Session就可以開(kāi)始正常的工作了,還是可以有效的提高開(kāi)發(fā)效率。

    Wicket類(lèi)加載的一個(gè)潛在問(wèn)題:

    如果僅僅使用Wicket開(kāi)發(fā)程序,那么Wicket1.3引入的熱加載機(jī)制,對(duì)開(kāi)發(fā)人員來(lái)說(shuō),會(huì)是一件非常幸福的事情,但如果同時(shí)使用了jsp,也就是說(shuō)同時(shí)在一個(gè)Web應(yīng)用程序(不是Web應(yīng)用服務(wù)器)中同時(shí)使用Wicket和JSP,而且在Wicket和JSP代碼分別向Session中寫(xiě)入相同的對(duì)象,如用戶信息之類(lèi)的數(shù)據(jù)對(duì)象,那么就會(huì)出現(xiàn)一些不必要的問(wèn)題,最覺(jué)的莫過(guò)于ClassCastException。雖然共用Wicket+JSP的情況比較少,出問(wèn)題的機(jī)率也比較少,但還是要額外提出來(lái)作為一個(gè)警示。

    下面是一個(gè)簡(jiǎn)單的類(lèi)Person代碼,用來(lái)展示如何出現(xiàn)ClassCastException:

    public class Person 

    {

    /**

     * Default constructor

     */

    public Person() 

    {

    super();

    ClassLoader classLoader = this.getClass().getClassLoader();

    while (null != classLoader) 

    {

    System.err.println("loader   " + classLoader.hashCode()+"   "+classLoader.getClass());

    classLoader = classLoader.getParent();

    }

    }

    }

    在構(gòu)造函數(shù)中的那段代碼,可以輸出它的ClassLoader順序,接下來(lái)象先前一樣訪問(wèn)那個(gè)HelloWorld應(yīng)用,得到如下的ClassLoader順序:

    loader   8310913   class org.apache.wicket.application.ReloadingClassLoader

    loader   3862294   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    然后再寫(xiě)一個(gè)run.jsp,代碼如下:

    <%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

    <%@page import="org.apache.wicket.examples.Person"%>

    <html>

    <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <title>Class Loader Demo</title>

    </head>

    <body>

    <%

    new Person();

    %>

    </body>

    </html>

    在run.jsp中,初始化一個(gè)Person對(duì)象,同樣觀察它的輸出結(jié)果,得到另外一個(gè)不同的Class加載順序:

    loader   3862294   class org.apache.catalina.loader.WebappClassLoader

    loader   19608393   class org.apache.catalina.loader.StandardClassLoader

    loader   13756574   class org.apache.catalina.loader.StandardClassLoader

    loader   26726999   class sun.misc.Launcher$AppClassLoader

    loader   7494106   class sun.misc.Launcher$ExtClassLoader

    可見(jiàn)在JSP中的Person與在HelloWorld中的Person是由不同的ClassLoader加載的(JSP編譯成Servlet執(zhí)行,在Tomcat中,org.apache.jasper.servlet.JasperLoader負(fù)責(zé)JSP編譯后的類(lèi)加載),根據(jù)JVM規(guī)范,這兩個(gè)Class是不等價(jià)的。因此進(jìn)行轉(zhuǎn)換的時(shí)候,會(huì)引起ClassCastException,這是特別需要注意的一點(diǎn)。


    點(diǎn)擊這里下載Word格式

    posted on 2008-11-24 11:44 豬兒笨笨 閱讀(1721) 評(píng)論(3)  編輯  收藏 所屬分類(lèi): Java開(kāi)發(fā)組件設(shè)計(jì)開(kāi)源軟件Wicket

    評(píng)論

    # re: Wicket1.3中Class熱加載--揭秘篇[未登錄](méi) 2008-11-25 10:18 beansoft

    Create a new classloader, as there is no way to clear a ClassLoader's cache.

    類(lèi)加載器里面的類(lèi)信息是無(wú)法被清理掉的, 這也是為什么reload幾次應(yīng)用就會(huì)出現(xiàn)OOM的原因, 個(gè)人認(rèn)為是每個(gè)類(lèi)的.class對(duì)象都無(wú)法被垃圾回收, 例如Spring+Hibernate會(huì)動(dòng)態(tài)生成N多類(lèi), 很容易導(dǎo)致perm溢出問(wèn)題.
    最近在看JavaRebel, 對(duì)免費(fèi)類(lèi)熱加載很感興趣, 因此打開(kāi)了一下 ReloadingClassLoader 的源碼, 其destory()方法的實(shí)現(xiàn)的確沒(méi)有辦法清除類(lèi)信息:
    /**
    * Remove the ModificationWatcher from the current reloading class loader
    */
    public void destroy()
    {
    watcher.destroy();
    }  回復(fù)  更多評(píng)論   

    # re: Wicket1.3中Class熱加載--揭秘篇[未登錄](méi) 2008-11-25 10:18 beansoft

    感謝提供好信息!! 不錯(cuò), 您研究的夠深入!  回復(fù)  更多評(píng)論   

    # re: Wicket1.3中Class熱加載--揭秘篇[未登錄](méi) 2008-11-25 12:14 豬兒笨笨

    是否可以被清除,這個(gè)其實(shí)在JVM中應(yīng)該是沒(méi)有規(guī)定的
    當(dāng)然Real JVM可能有所規(guī)定
    但我估計(jì)對(duì)于自動(dòng)GC機(jī)制,很難準(zhǔn)確分析哪些類(lèi)沒(méi)有被占用,或者即使分析成本也比較高,所以常見(jiàn)的JVM是沒(méi)有處理的。
    雖然沒(méi)有能夠釋放,但是對(duì)于開(kāi)放人員來(lái)講,這個(gè)功能還是非常實(shí)用的。
    所以我在上篇中也說(shuō),這個(gè)功能在開(kāi)發(fā)時(shí)用用OK,千萬(wàn)不要在上線系統(tǒng)中用。  回復(fù)  更多評(píng)論   

    導(dǎo)航

    統(tǒng)計(jì)

    常用鏈接

    留言簿(18)

    隨筆分類(lèi)

    隨筆檔案

    文章分類(lèi)

    文章檔案

    搜索

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 日韩免费在线观看视频| 无码人妻精品一二三区免费| 亚洲国产第一页www| 成人免费一级毛片在线播放视频| 亚洲GV天堂GV无码男同| 亚洲老妈激情一区二区三区| 成人免费的性色视频| 免费亚洲视频在线观看| 亚洲AV无一区二区三区久久| 好男人看视频免费2019中文| 九九九精品视频免费| 久久久久亚洲AV无码麻豆| 午夜视频在线在免费| 日本高清高色视频免费| 亚洲AV色欲色欲WWW| 亚洲AV综合色一区二区三区| 在线观看免费a∨网站| 日本免费A级毛一片| 亚洲国产精品美女久久久久| 亚洲精品无码国产| 国产资源免费观看| 一级毛片免费不卡在线| 免费的黄色网页在线免费观看| 亚洲国产综合人成综合网站00| 亚洲午夜无码片在线观看影院猛| 日韩不卡免费视频| a级毛片在线免费| 边摸边吃奶边做爽免费视频网站 | 13小箩利洗澡无码视频网站免费| 亚洲中文字幕乱码AV波多JI| 亚洲午夜福利在线观看| 国产亚洲福利一区二区免费看| 亚洲一区免费视频| 免费人成激情视频在线观看冫| 美女裸免费观看网站| 亚洲国产精品无码久久久| 久久精品国产亚洲AV麻豆~| 亚洲?V乱码久久精品蜜桃 | 欧美色欧美亚洲另类二区| 亚洲网址在线观看你懂的| 亚洲国产一区二区视频网站|