細看JSP應用運行中與Servlet的關系
經常有朋友問起,JSP和Servlet之間有什么區(qū)別,兩者之間又有什么聯系?其實Servlet技術的出現時間很早,是當時為了Java的服務器端應用而開發(fā)的。大家都知道Applet是應用小程序,Servlet就是服務器端小程序了。但在Microsoft公司的ASP技術出現后,使用Servlet進行響應輸出時一行行的輸出語句就顯得非常笨拙,對于復雜布局或者顯示頁面更是如此。JSP就是為了滿足這種需求在Servlet技術之上開發(fā)的??梢姡琂SP和Servlet之間有著內在的血緣關系,在學習JSP時,如果能夠抓住這種聯系,就能更深刻地理解JSP應用的運行機理,達到事半功倍的效果。
本文將通過對一個JSP應用運行過程的剖析,深入JSP運行的內幕,并從全新的視角闡述一些JSP中的技術要點。
HelloWorld.jsp
我們以Tomcat 4.1.17服務器為例,來看看最簡單的HelloWorld.jsp應用程序是怎么運行的。
代碼清單1:HelloWorld.jsp
HelloWorld.jsp
- < %
- String message = "Hello World!";
- %>
- < %=message%>
這個文件非常簡單,僅僅定義了一個String的變量,并且輸出。把這個文件放到Tomcat的webappsROOT目錄下,啟動Tomcat,在瀏覽器中訪問http://localhost:8080/HelloWorld.jsp,瀏覽器中的輸出為“HelloWorld!”
讓我們來看看Tomcat都做了什么。轉到Tomcat的workStandalonelocalhost_目錄下,可以找到如下的HelloWorld_jsp.java,這個文件就是Tomcat解析HelloWorld.jsp時生成的源文件:
代碼清單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在運行時首先解析成一個Java類HelloWorld_jsp.java,該類繼承于org.apache.jasper.runtime.HttpJspBase基類,HttpJspBase實現了HttpServlet接口。可見,JSP應用在運行前首先將編譯為一個Servlet,這就是理解JSP技術的關鍵。
我們還知道JSP頁面中內置了幾個對象,如pageContext、application、config、page、session、out等,你可能會奇怪,為什么在JSP中的代碼片斷中可以直接使用這些內置對象。觀察_jspService()方法,實際上這幾個內置對象就是在這里定義的。在對JSP文件中的代碼片斷進行解析之前,先對這幾個內置對象進行初始化。
首先,調用JspFactory的getDefaultFactory()方法獲取容器實現(本文中指Tomcat 4.1.17)的一個JspFactory對象的引用。JspFactory是javax.servlet.jsp包中定義的一個抽象類,其中定義了兩個靜態(tài)方法set/getDefaultFactory()。set方法由JSP容器(Tomcat)實例化該頁面Servlet(即HelloWorld_jsp類)的時候置入,所以可以直接調用JspFactory.getDefaultFactory()方法得到這個JSP工廠的實現類。Tomcat是調用org.apache.jasper.runtime.JspFactoryImpl類。
然后,調用這個JspFactoryImpl的getPageContext()方法,填充一個PageContext返回,并賦給內置變量pageConext。其它內置對象都經由該pageContext得到。具體過程見上面的代碼,這里不再贅述。該頁面Servlet的環(huán)境設置完畢,開始對頁面進行解析。HelloWorld.jsp頁面僅僅定義了一個String變量,然后直接輸出。解析后的代碼如下:
代碼清單3:JSP頁面解析后的代碼片斷
- String message = "Hello World!";
- out.print(message);
以上,JSP應用的運行過程應該很清楚了。
【編輯推薦】