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

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

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

    隨筆-295  評(píng)論-26  文章-1  trackbacks-0
    JSP的運(yùn)行內(nèi)幕

    文章作者:
    責(zé)任編輯: 錄入時(shí)間:2004-11-1 18:26:13 來(lái)源:第七頻道
    頻道聲明:本頻道的文章除部分特別聲明禁止轉(zhuǎn)載的專稿外,可以自由轉(zhuǎn)載.但請(qǐng)務(wù)必注明出出處和原始作者 文章版權(quán)歸本頻道與文章作者所有.對(duì)于被頻道轉(zhuǎn)載文章的個(gè)人和網(wǎng)站,我們表示深深的謝意.?

    經(jīng)常有朋友問(wèn)起,JSP和Servlet之間有什么區(qū)別,兩者之間又有什么聯(lián)系?其實(shí)Servlet技術(shù)的出現(xiàn)時(shí)間很早,是當(dāng)時(shí)為了Java的服務(wù)器端應(yīng)用而開(kāi)發(fā)的。大家都知道Applet是應(yīng)用小程序,Servlet就是服務(wù)器端小程序了。但在Microsoft公司的ASP技術(shù)出現(xiàn)后,使用Servlet進(jìn)行響應(yīng)輸出時(shí)一行行的輸出語(yǔ)句就顯得非常笨拙,對(duì)于復(fù)雜布局或者顯示頁(yè)面更是如此。JSP就是為了滿足這種需求在Servlet技術(shù)之上開(kāi)發(fā)的。可見(jiàn),JSP和Servlet之間有著內(nèi)在的血緣關(guān)系,在學(xué)習(xí)JSP時(shí),如果能夠抓住這種聯(lián)系,就能更深刻地理解JSP的運(yùn)行機(jī)理,達(dá)到事半功倍的效果。

    本文將通過(guò)對(duì)一個(gè)JSP運(yùn)行過(guò)程的剖析,深入JSP運(yùn)行的內(nèi)幕,并從全新的視角闡述一些JSP中的技術(shù)要點(diǎn)。

    HelloWorld.jsp

    我們以Tomcat 4.1.17服務(wù)器為例,來(lái)看看最簡(jiǎn)單的HelloWorld.jsp是怎么運(yùn)行的。

    代碼清單1:HelloWorld.jsp

    HelloWorld.jsp
    <%
     String message = "Hello World!";
    %>
    <%=message%>


      這個(gè)文件非常簡(jiǎn)單,僅僅定義了一個(gè)String的變量,并且輸出。把這個(gè)文件放到Tomcat的webapps\ROOT\目錄下,啟動(dòng)Tomcat,在瀏覽器中訪問(wèn)http://localhost:8080/HelloWorld.jsp,瀏覽器中的輸出為“HelloWorld!”

      讓我們來(lái)看看Tomcat都做了什么。轉(zhuǎn)到Tomcat的\work\Standalone\localhost\_目錄下,可以找到如下的HelloWorld_jsp.java,這個(gè)文件就是Tomcat解析HelloWorld.jsp時(shí)生成的源文件:

      代碼清單2:HelloWorld_jsp.java

    package org.apache.jsp;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.jsp.*;
    import org.apache.jasper.runtime.*;
    
    public class HelloWorld_jsp extends HttpJspBase {
     ......
    public void _jspService(HttpServletRequest request, 
    HttpServletResponse response)throws java.io.IOException, ServletException
     {
      JspFactory _jspxFactory = null;
      javax.servlet.jsp.PageContext pageContext = null;
      HttpSession session = null;
      ServletContext application = null;
      ServletConfig config = null;
      JspWriter out = null;
      Object page = this;
      JspWriter _jspx_out = null;
    
      try {
       _jspxFactory = JspFactory.getDefaultFactory();
       response.setContentType("text/html;charset=ISO-8859-1");
       pageContext = _jspxFactory.getPageContext(this, request, 
    response,null, true, 8192, true); application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; String message = "Hello World!"; out.print(message); } catch (Throwable t) { out = _jspx_out; if (out != null && out.getBufferSize() != 0) out.clearBuffer(); if (pageContext != null) pageContext.handlePageException(t); } finally { if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext); } } }


      從上面可以看出,HelloWorld.jsp在運(yùn)行時(shí)首先解析成一個(gè)Java類HelloWorld_jsp.java,該類繼承于org.apache.jasper.runtime.HttpJspBase基類,HttpJspBase實(shí)現(xiàn)了HttpServlet接口。可見(jiàn),JSP在運(yùn)行前首先將編譯為一個(gè)Servlet,這就是理解JSP技術(shù)的關(guān)鍵。

      我們還知道JSP頁(yè)面中內(nèi)置了幾個(gè)對(duì)象,如pageContext、application、config、page、session、out等,你可能會(huì)奇怪,為什么在JSP中的代碼片斷中可以直接使用這些內(nèi)置對(duì)象。觀察_jspService()方法,實(shí)際上這幾個(gè)內(nèi)置對(duì)象就是在這里定義的。在對(duì)JSP文件中的代碼片斷進(jìn)行解析之前,先對(duì)這幾個(gè)內(nèi)置對(duì)象進(jìn)行初始化。

      首先,調(diào)用JspFactory的getDefaultFactory()方法獲取容器實(shí)現(xiàn)(本文中指Tomcat 4.1.17)的一個(gè)JspFactory對(duì)象的引用。JspFactory是javax.servlet.jsp包中定義的一個(gè)抽象類,其中定義了兩個(gè)靜態(tài)方法set/getDefaultFactory()。set方法由JSP容器(Tomcat)實(shí)例化該頁(yè)面Servlet(即HelloWorld_jsp類)的時(shí)候置入,所以可以直接調(diào)用JspFactory.getDefaultFactory()方法得到這個(gè)JSP工廠的實(shí)現(xiàn)類。Tomcat是調(diào)用org.apache.jasper.runtime.JspFactoryImpl類。

      然后,調(diào)用這個(gè)JspFactoryImpl的getPageContext()方法,填充一個(gè)PageContext返回,并賦給內(nèi)置變量pageConext。其它內(nèi)置對(duì)象都經(jīng)由該pageContext得到。具體過(guò)程見(jiàn)上面的代碼,這里不再贅述。該頁(yè)面Servlet的環(huán)境設(shè)置完畢,開(kāi)始對(duì)頁(yè)面進(jìn)行解析。HelloWorld.jsp頁(yè)面僅僅定義了一個(gè)String變量,然后直接輸出。解析后的代碼如下:

      代碼清單3:JSP頁(yè)面解析后的代碼片斷

    String message = "Hello World!";
    out.print(message);


      定制標(biāo)簽的解析過(guò)程

      在一個(gè)中大型的Web應(yīng)用中,通常使用JSP定制標(biāo)簽來(lái)封裝頁(yè)面顯示邏輯。剖析容器對(duì)定制標(biāo)簽的解析過(guò)程,對(duì)我們深入理解定制標(biāo)簽的運(yùn)行機(jī)理非常有幫助。下面我們以Struts1.1b中附帶的struts-example應(yīng)用的主頁(yè)運(yùn)行為例加以說(shuō)明。

      包含定制標(biāo)簽的index.jsp

      Struts1.1b的下載地址是http://jakarta.apache.org/struts/index.html。將下載的包解壓,在webapps目錄下可以找到struts-example.war。將該War包拷貝到Tomcat的webapps目錄下,Tomcat會(huì)自動(dòng)安裝此應(yīng)用包。在瀏覽器中通過(guò)http://localhost:8080/struts-example訪問(wèn)struts-example應(yīng)用,將顯示應(yīng)用的首頁(yè)(見(jiàn)圖1)。



      圖一 應(yīng)用的首頁(yè)

      代碼清單4:index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
    <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
    <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
    <html:html locale="true">
    <head>
    <title><bean:message key="index.title"/></title>
    <html:base/>
    </head>
    <body bgcolor="white">
    …… 
    </body>
    </html:html>


      我們僅以index.jsp中的<bean:message/>標(biāo)簽的解析為例進(jìn)行分析,看容器是怎樣把這個(gè)自定義標(biāo)簽解析成HTML輸出的。上面代碼省略了頁(yè)面的其它顯示部分。首先,查看上面瀏覽器中頁(yè)面的源文件:

    <html lang="zh">
    <head>
    <title>MailReader Demonstration Application (Struts 1.0)</title>
    </head>
    <body bgcolor="white">
    ……
    </body>
    </html>


      可見(jiàn),容器已經(jīng)把<bean:message key="index.title"/>替換成一個(gè)字串,顯示為頁(yè)面的標(biāo)題。

      解析過(guò)程

      那么,JSP容器是怎樣完成解析的呢?查看在工作目錄jakarta-tomcat-4.1.17\work\Standalone\localhost\struts-example下解析后的index_jsp.java文件:

      代碼清單5:index_jsp.java

    package org.apache.jsp;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.jsp.*;
    import org.apache.jasper.runtime.*;
    public class index_jsp extends HttpJspBase {
     //為所有的定制標(biāo)簽定義處理器池類的引用
     private org.apache.jasper.runtime.TagHandlerPool ;
     _jspx_tagPool_bean_message_key;
     ……
     //頁(yè)面類構(gòu)造方法
     public index_jsp() {
      _jspx_tagPool_bean_message_key =
      new org.apache.jasper.runtime.TagHandlerPool();
       ……
     }
    
     public void _jspService(HttpServletRequest request,
       HttpServletResponse response) 
       throws java.io.IOException, ServletException {
      ……
      _jspxFactory = JspFactory.getDefaultFactory();
      response.setContentType("text/html;charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this,
        request, response,null, true, 8192, true);
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
      ……
      if (_jspx_meth_html_html_0(pageContext))
      return;
      ……
     }
     //頁(yè)面在處理退出時(shí)釋放所有定制標(biāo)簽的屬性
     public void _jspDestroy() {
      _jspx_tagPool_bean_message_key.release();
      ……
     }
    }


      生成的index_jsp.java繼承于org.apache. jasper.runtime.HttpJspBase。研究這個(gè)文件為我們了解定制標(biāo)簽的運(yùn)行機(jī)理提供了途徑。

      從上面可以看出,Tomcat在解析一個(gè)JSP頁(yè)面時(shí),首先為每一個(gè)定制標(biāo)簽定義并實(shí)例化了一個(gè)TagHandlerPool對(duì)象。頁(yè)面的處理方法覆蓋父類的_ jspService()方法,_jspService方法首先初始化環(huán)境,為內(nèi)置對(duì)象賦值。由于index.jsp頁(yè)面整體由一個(gè)<html:html/>標(biāo)簽包裹,Tomcat對(duì)每一個(gè)標(biāo)簽都產(chǎn)生一個(gè)私有方法加以實(shí)現(xiàn)。<html:html/>標(biāo)簽的處理方法是_jspx_meth_html_html_0()。這個(gè)方法的命名規(guī)范大家也可以從這里看出,就是“_jspx_meth + 標(biāo)簽的前綴 + 標(biāo)簽名 + 該標(biāo)簽在JSP頁(yè)面同類標(biāo)簽中出現(xiàn)的序號(hào)”。其它標(biāo)簽都被包含在該標(biāo)簽中,所以其它標(biāo)簽在_jspx_meth_html_html_0()方法中進(jìn)行解析。具體的代碼實(shí)現(xiàn)請(qǐng)參見(jiàn)賽迪網(wǎng)http://linux.ccidnet.com期刊瀏覽2003年第6期。

      在_jspx_meth_html_html_0()方法中,首先從_jspx_tagPool_html_html_locale池中得到一個(gè)org.apache.struts.taglib.html.HtmlTag的實(shí)例,然后設(shè)置這個(gè)tag實(shí)例的頁(yè)面上下文及上級(jí)標(biāo)簽,由于html:html標(biāo)簽是頁(yè)面的最頂層標(biāo)簽,所以它的parent是null。然后對(duì)該標(biāo)簽的內(nèi)容進(jìn)行解析。HTML代碼直接輸出,下面主要看看<html:html></html:html>標(biāo)簽之間包含的<bean:message key="index.title"/>標(biāo)簽的解析。對(duì)bean:message標(biāo)簽的解析類似于html:html,Tomcat也將其放入一個(gè)單獨(dú)的方法_jspx_meth_bean_message_0()中進(jìn)行。

      bean:message標(biāo)簽的解析

      代碼清單7:_jspx_meth_bean_message_0()方法片斷

    //對(duì)message定制標(biāo)簽的處理方法
    private boolean _jspx_meth_bean_message_0(
    javax.servlet.jsp.tagext.Tag _jspx_th_html_html_0, 
    javax.servlet.jsp.PageContext pageContext) throws Throwable {
     JspWriter out = pageContext.getOut();
     /* ----  bean:message ---- */
     org.apache.struts.taglib.bean.MessageTag
     _jspx_th_bean_message_0 =
     (org.apache.struts.taglib.bean.MessageTag) 
     _jspx_tagPool_bean_message_key.get(
     org.apache.struts.taglib.bean.MessageTag.class);
     _jspx_th_bean_message_0.setPageContext(pageContext);
     _jspx_th_bean_message_0.setParent(_jspx_th_html_html_0);
     _jspx_th_bean_message_0.setKey("index.title");
     int _jspx_eval_bean_message_0 = _jspx_th_bean_message_0.doStartTag();
     if (_jspx_th_bean_message_0.doEndTag()== javax.servlet.jsp.
    tagext.Tag.SKIP_PAGE) return true; _jspx_tagPool_bean_message_key.reuse(_jspx_th_bean_message_0); return false; }


      同樣,對(duì)html:bean也需要從池中得到一個(gè)標(biāo)簽類的實(shí)例,然后設(shè)置環(huán)境。這里不再贅述。我們只專注對(duì)MessageTag定制標(biāo)簽類特殊的處理部分。定制標(biāo)簽類的開(kāi)發(fā)不在本文討論范圍之內(nèi)。在index.jsp中定義了一個(gè)bean:message標(biāo)簽,并設(shè)置了一個(gè)屬性:<bean:message key="index.title"/>。Tomcat在解析時(shí),調(diào)用MessageTag對(duì)象的key屬性設(shè)置方法setKey(),將該屬性置入。然后調(diào)用MessageTag的doStartTag()和doEndTag()方法,完成解析。如果doEndTag()方法的返回值為javax.servlet.jsp.tagext.Tag. SKIP_PAGE,表明已經(jīng)完成解析,返回true,Tomcat將立即停止剩余頁(yè)面代碼的執(zhí)行,并返回。否則把該MessageTag的實(shí)例放回池中。

      標(biāo)簽類對(duì)象實(shí)例的池化

      為了提高運(yùn)行效率,Tomcat對(duì)所有的定制標(biāo)簽類進(jìn)行了池化,池化工作由org.apache.jasper. runtime.TagHandlerPool類完成。TagHandlerPool類主要有兩個(gè)方法,代碼如下:

      代碼清單8:TagHandlerPool.java

    public class TagHandlerPool {
     private static final int MAX_POOL_SIZE = 5;
     private Tag[] handlers;
     public synchronized Tag get(Class handlerClass) throws JspException {……}
     public synchronized void reuse(Tag handler) {……}
    }


      TagHandlerPool簡(jiǎn)單地實(shí)現(xiàn)了對(duì)標(biāo)簽類的池化,其中MAX_POOL_SIZE是池的初始大小,handlers是一個(gè)Tag的數(shù)組,存儲(chǔ)標(biāo)簽類的實(shí)例。get(Class handlerClass)得到一個(gè)指定標(biāo)簽類的實(shí)例,如果池中沒(méi)有可用實(shí)例,則新實(shí)例化一個(gè)。reuse(Tag handler)把handler對(duì)象放回池中。

      至此,我們對(duì)JSP在容器中的運(yùn)行過(guò)程已經(jīng)了然于胸了。雖然每種JSP容器的解析結(jié)果會(huì)有差異,但其中的原理都雷同。對(duì)于編寫(xiě)JSP應(yīng)用,我們并不需要干涉容器中的運(yùn)行過(guò)程,但如果你對(duì)整個(gè)底層的運(yùn)行機(jī)制比較熟悉,就能對(duì)JSP/Servlet技術(shù)有更深的認(rèn)識(shí)。


    大盤(pán)預(yù)測(cè) 國(guó)富論
    posted on 2007-09-10 16:53 華夢(mèng)行 閱讀(194) 評(píng)論(0)  編輯  收藏 所屬分類: Tomcat

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 成人性生交大片免费看无遮挡| 亚洲国产成人精品女人久久久| 亚洲区日韩精品中文字幕| 免费A级毛片无码A| 免费成人在线视频观看| 亚洲人成网站看在线播放| 亚洲中文字幕无码专区| 18禁成人网站免费观看| 国产偷国产偷亚洲高清人| 亚洲AV日韩AV天堂久久| 日韩免费一级毛片| 99久久综合精品免费 | 久久精品亚洲日本波多野结衣| 亚洲国产一区二区三区| 午夜性色一区二区三区免费不卡视频 | 久久久综合亚洲色一区二区三区 | 亚洲v国产v天堂a无码久久| 日本免费一区二区三区| 国产精品亚洲lv粉色| 久久久久亚洲av无码专区| 深夜国产福利99亚洲视频| 131美女爱做免费毛片| 一级做a爰性色毛片免费| 亚洲色欲色欲www| 亚洲国产精品VA在线观看麻豆| 成年性羞羞视频免费观看无限| 在线观看黄片免费入口不卡| 亚洲AV无码精品国产成人| 久久综合亚洲色一区二区三区| 亚洲毛片网址在线观看中文字幕| 永久免费的网站在线观看| 午夜无码A级毛片免费视频| 亚州**色毛片免费观看| 亚洲AV香蕉一区区二区三区| 亚洲色偷偷av男人的天堂| 亚洲人成人77777网站| 亚洲成A人片77777国产| 永久免费av无码网站大全| 国产成人免费网站| 最近中文字幕2019高清免费| 特级做A爰片毛片免费看无码|