1. 程式人生 > >JSP與Servlet的關系理解

JSP與Servlet的關系理解

run 新的 into 開始 線程 服務器 選擇 bin .exe

Servlet是Java提供的用於開發Web服務器應用程序的一個組件,運行在服務器端,由Servlet容器所管理,用於生成動態的內容。Servlet是平臺獨立的Java類,編寫一個Servlet,實際上就是按照Servlet規範編寫一個Java類。

技術分享

如圖所示,Java提供一系列接口類(所謂接口類就是類中所有方法只提供方法聲明,不提供任何的方法實現,這些類的實現就留給後繼者去做。):Servlet、ServletConfig、Serializable,然後通過多重繼承產生一個最通用的Servlet實現類(圖中Gerneric Servlet類),接下來,通過一個多重繼承與實現,產生一個新的實現類HttpServlet,用戶在開發Servlet程序時只需繼承這個類,從而產生一個自己的類(圖中Hello_Servlet類),然後根據實際開發功能與信息處理需要,去實現該類中的相關方法即可。這就是前面提到的按照Servlet規範編寫一個Java類,從而編寫一個Servlet。

至於JSP(JavaServlet Page)從圖中可以看出,實際上它也是從Servlet繼承而來。只不過它在Servlet當中又添加/修改了一些方法,作了新的封裝。具體到Tomcat Web應用服務器中,它通過一個多重繼承,分別從Java的HttpJspPage和HttpServlet兩個類那裏繼承和實現一些方法,然後封裝一個叫做HttpJspBase的類從而實現了一個通用化的JSP類,用戶在開發自己的JSP時,只需要從HttpJspBase繼承一個自己的類(如圖中Hello_jsp類),然後根據需要去實現相應的方法即可。

因此這也是為什麽JSP的代碼中總是閃現Servlet代碼框架影子的原因,其實它們只是為實現同樣的功能而進行了不同封裝的組件而已,血脈裏留著的是一樣的血。

“既生瑜何生亮?”呵呵,因為JSP確實比Servlet要更勝一籌,所謂“青出於藍勝於藍”,既然Sun公司要在Servlet基礎上推出JSP技術,那肯定是因為JSP有它更高明的地方。

使用Servlet產生動態網頁,需要在代碼中打印輸出很多HTML的標簽,此外,在Servlet中,我們不得不將靜態現實的內容和動態產生內容的代碼混合在一起。使用Servlet開發動態網頁,程序員和網頁編輯人員將無法一起工作,因為網頁編輯人員不了解Java語言,無法修改Servlet代碼,而Java程序員可能也不是很了解網頁編輯人員的意圖,以至於無法修改和實現網頁功能。為了解決這些問題,Sun公司就推出了JSP技術。

JSP是Servlet的擴展,在沒有JSP之前,就已經出現了Servlet技術。Servlet是利用輸出流動態生成HTML頁面,包括每一個HTML標簽和每個在HTML頁面中出現的內容。

JSP通過在標準的HTML頁面中插入Java代碼,其靜態的部分無須Java程序控制,只有那些需要從數據庫讀取並根據程序動態生成信息時,才使用Java腳本控制。

事實上,JSP是Servlet的一種特殊形式,每個JSP頁面就是一個Servlet實例——JSP頁面由系統編譯成Servlet,Servlet再負責響應用戶請求。JSP其實也是Servlet的一種簡化,使用JSP時,其實還是使用Servlet,因為Web應用中的每個JSP頁面都會由Servlet容器生成對應的Servlet。對於Tomcat而言,JSP頁面生成的Servlet放在work路徑對應的Web應用下。

以apache-tomcat-7.0.37\webapps\myapp\index.jsp為例,

[html] view plain copy
  1. <html>
  2. <body>
  3. <center>
  4. Now time is: <%=new java.util.Date()%>
  5. </center>
  6. </body>
  7. </html>

當啟動Tomcat之後,可以在Tomcat的apache-tomcat-7.0.37\work\Catalina\localhost\myapp\org\apache\jsp目錄下找到如下文件:indexd.java和index.class。這兩個文件都是Tomcat生成的,Tomcat根據JSP頁面生成對應Servlet的Java文件及class文件。

index.java

[html] view plain copy
  1. //JSP頁面經過Tomcat編譯後默認的包
  2. package org.apache.jsp;
  3. import javax.servlet.*;
  4. import javax.servlet.http.*;
  5. import javax.servlet.jsp.*;
  6. //繼承HttpJspBase類,該類其實是個Servlet的子類
  7. public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
  8. implements org.apache.jasper.runtime.JspSourceDependent {
  9. private static final javax.servlet.jsp.JspFactory _jspxFactory =
  10. javax.servlet.jsp.JspFactory.getDefaultFactory();
  11. private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
  12. private javax.el.ExpressionFactory _el_expressionfactory;
  13. private org.apache.tomcat.InstanceManager _jsp_instancemanager;
  14. public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
  15. return _jspx_dependants;
  16. }
  17. public void _jspInit() {
  18. _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
  19. _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
  20. }
  21. public void _jspDestroy() {
  22. }
  23. //用於響應用戶的方法
  24. public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
  25. throws java.io.IOException, javax.servlet.ServletException {
  26. final javax.servlet.jsp.PageContext pageContext;
  27. javax.servlet.http.HttpSession session = null;
  28. final javax.servlet.ServletContext application;
  29. final javax.servlet.ServletConfig config;
  30. //獲得頁面輸出流
  31. javax.servlet.jsp.JspWriter out = null;
  32. final java.lang.Object page = this;
  33. javax.servlet.jsp.JspWriter _jspx_out = null;
  34. javax.servlet.jsp.PageContext _jspx_page_context = null;
  35. //開始生成響應
  36. try {
  37. //設置輸出的頁面格式
  38. response.setContentType("text/html");
  39. pageContext = _jspxFactory.getPageContext(this, request, response,
  40. null, true, 8192, true);
  41. _jspx_page_context = pageContext;
  42. application = pageContext.getServletContext();
  43. config = pageContext.getServletConfig();
  44. session = pageContext.getSession();
  45. //頁面輸出流
  46. out = pageContext.getOut();
  47. _jspx_out = out;
  48. //輸出流,開始輸出頁面文檔
  49. out.write("<html>\r\n");
  50. out.write("<body>\r\n");
  51. out.write("<center> \r\n");
  52. out.write("Now time is: ");
  53. out.print(new java.util.Date());
  54. out.write(" \r\n");
  55. out.write("</center>\r\n");
  56. out.write("</body>\r\n");
  57. out.write("</html>");
  58. } catch (java.lang.Throwable t) {
  59. if (!(t instanceof javax.servlet.jsp.SkipPageException)){
  60. out = _jspx_out;
  61. if (out != null && out.getBufferSize() != 0)
  62. try { out.clearBuffer(); } catch (java.io.IOException e) {}
  63. if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
  64. else throw new ServletException(t);
  65. }
  66. } finally {
  67. _jspxFactory.releasePageContext(_jspx_page_context);
  68. }
  69. }
  70. }

JSP頁面中內置了幾個對象,如pageContext、application、config、page、session、out等_jspService()方法,這幾個內置對象就是在這裏定義的。

根據上面的JSP頁面工作原理圖,可以得到如下結論: JSP文件必須在JSP服務器內運行。JSP文件必須生成Servlet才能執行。每個JSP頁面的第一個訪問者速度很慢,因為必須等待JSP編譯成Servlet。JSP頁面的訪問者無須安裝任何客戶端,甚至不需要可以運行Java的運行環境,因為JSP頁面輸送到客戶端的是標準HTML頁面。index.jsp頁面中的每個字符都由index.java文件的輸出流生成.
servlet是在web服務器上的java程序,它提供服務,由它來傳遞給你html的格式。Servlet API為Servlet提供了統一的編程接口
Servlet必須部署在Servlet容器,才能響應客戶端的請求 對外提供服務。要對外統一接口,由容器來調用。
jsp側重顯示;servlet側重控制邏輯。
MVC模式:Jsp + Servlet + JavaBean。M-JavaBean V-Jsp C-Servlet
小應用程序(Applet)是指采用Java創建的基於HTML的程序。瀏覽器將其暫時下載到用戶的硬盤上,並在Web頁打開時在本地運行。們可以直接嵌入到網頁或者其他特定的容器中,並能夠產生特殊的效果。

所有基於Java的服務器端編程都是構建在Servlet之上的。在J2EE中Servlet已經是一個標準的組件。

目前,Servlet引擎一般是第三方的插件,它通過一定的方法連接到Web服務器,Servlet引擎把它識別為Servlet請求的那些HTTP請求截獲下來處理,而其他的HTTP請求由Web服務器按照通常的方式來處理,Servlet引擎會裝載合適的Servlet到內存中,如果Servlet還沒有運行的話,會分配一個可以使用的線程來處理請求,再把Servlet的輸出返回到發出請求的Web客戶機。

Java Servlet和Java Applet正好是相對應的兩種程序類型,Applet運行在客戶端,在瀏覽器內執行,而Servlet在服務器內部運行,通過客戶端提交的請求啟動運行。

同樣的Servlet完全可以在Apache,IIS等不同Web服務器上執行,不管底層的操作系統是Windows,Solaris,Mac,Linux。

Java Servlet有著十分廣泛的應用。使用Servlet還可以實現大量的服務器端的管理維護功能,以及各種特殊的任務,比如,並發處理多個請求,轉送請求,代理等。

典型的Servlet運行環境有JSWDK,Tomcat,Resin等,。它們都自帶一個簡單的HTTP Server,只需簡單配置即可投入使用,你也可以把它們綁定到常用的Web服務器上,如Apache,IIS等,提供小規模的Web服務。還有一些商業的大中型的支持Servlet和JSP的Web服務器,如JRun,Web Sphere,Web Logic等等,配置比較復雜,並不適合初學者。但是功能較為強大,有條件的讀者可以一試。

[html] view plain copy
  1. import java.io.*;
  2.  import java.util.*;
  3.  import javax.servlet.http.*;
  4.  import javax.servlet.*;
  5.  //導入必要的包
  6.  public class HelloServlet extends HttpServlet {
  7.    //所有Servlet必須從HttpServlet派生
  8.    public void doGet (HttpServletRequest req, HttpServletResponse res)
  9.    throws ServletException, IOException
  10.    //doGet()是這個Servlet的核心,真正處理請求的地方
  11.  {
  12.  res.setContentType("text/html");
  13.    //設置相應的類型為text/html
  14.  PrintWriter pw = res.getWriter();
  15.    //從HttpServletResponse得到輸出流   
  16.  pw.println("<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">");
  17.  pw.println("<head>");
  18.  pw.println("<meta http-equiv=""Content-Type"" content=""text/html; charset=ISO-8859-1"">");
  19.  pw.println("<!-- The Servlet expression tags interpolate script variables into the HTML -->");
  20.  pw.println("<title>Hello, world!</title>");
  21.  pw.println("</head>");
  22.  pw.println("<body bgcolor=#cc99dd>");
  23.  pw.println("<h1>Hello, world!</h1>");
  24.  pw.println("</body>");
  25.  //上面的語句都是向客戶端打印HTML文本
  26.  pw.close();
  27.  //關閉HttpServletResponse,使Web服務器知道相應結束
  28. }
  29. public HelloServlet() {} //構造函數,可以不要

Servlet與表單交互的方法
Servlet使用HttpServlet類中的方法與表單進行交互。在HttpServlet類中有幾個未完全實現的方法,你可以自己定義方法的內容,但是必須正確使用方法名稱以使HTTP Server把客戶請求正確的映射到相應的函數上。
  doHeader 用於處理HEADER請求
  doGet 用於處理GET請求,也可以自動的支持HEADER請求
  doPost 用於處理POST請求
  doPut 用於處理PUT請求
  doDelete 用於處理DELETE請求

在使用這些方法時必須帶兩個參數。第一個包含來自客戶端的數據HttpServletRequest。第二個參數包含客戶端的相應HttpServletResponse。

一個HttpServletRequest對象提供請求HTTP頭部數據,也允許獲取客戶端的數據。怎樣獲取這些數據取決於HTTP請求方法。不管何種HTTP方式,都可以用getParameterValues方法返回特定名稱的參數值。HttpServletRequest,HttpServletResponse接口分別繼承於ServletRequest和ServletResponse接口,getParameterValues和getWriter方法都是其祖先接口中的方法。

對於HTTP GET請求的方式,getQueryString方法將會返回一個可以用來解剖分析的參數值。
  對於用HTTP POST,PUT和DELETE請求的方式,HttpServletRequest有兩種方法可以選擇:如果是文本數據,你能通過getReader的方法得到BufferedReader獲取數據;如果是二進制數據,可以通過getInputStream方法得到ServletInputStream獲取數據。
  為了相應客戶端,一個HttpServletResponse對象提供返回數據給用戶的兩個方法:一種是用getWriter方法得到一個PrintWriter,用於返回文本數據;另一種方法是用getOutputStream方法得到ServletOutputStream,用於返回二進制數據。在使用Writer或OutputStream之前應先設置頭部(HttpServletResponse中有相應的方法),然後用Writer或OutputStream將相應的主體部分發給用戶。完成後要關閉Writer或OutputStream以便讓服務器知道相應已經結束。

在進行HTTP網絡傳輸的時候,統一采用的編碼方式是ISO-8859-1

字符編碼轉換常用的方法是
  String native_encoded = "中文字符串"; //本地編碼的字符串
  Byte[] byte_array = native_encoded.getBytes(); //得到本地編碼的字節數組
  String net_encoded = new String(native_encoded, "ISO-8859-1"); //生成ISO-8859-1編碼的字符串

例:out.println(new String(new String("<td>你的姓名:</td>").getBytes(),"ISO-8859-1"));

用Servlet控制會話

會話狀態的維持是開發Web應用所必須面對的問題,有多種方法可以來解決這個問題,如使用Cookies,或直接把狀態信息加到URL中等,還有Servlet本身提供了一個HttpSession接口來支持會話狀態的維持 。

Session的發明是為了填補HTTP協議的局限。

從服務器這端來看,每一個請求都是獨立的,因此HTTP協議被認為是無狀態協議,當用戶在多個主頁間切換時,服務器無法知道他的身份。Session的出現就是為了彌補這個局限。利用Session,您就可以當一個用戶在多個主頁間切換的時候也能保存他的信息。這樣很多以前根本無法去做的事情就變得簡單多了。
在訪問者從到達某個特定的主頁到離開為止的那段時間,每個訪問者都會單獨獲得一個Session

Java Servlet定義了一個HttpSession接口,實現的Session的功能,在Servlet中使用Session的過程如下:
  (1) 使用HttpServletRequest的getSession方法得到當前存在的session,如果當前沒有定義session,則創建一個新的session,還可以使用方法getSession(true)
  (2) 寫session變量。可以使用方法HttpSession.setAttribute(name,value)來向Session中存儲一個信息。
  (3) 讀Session變量。可以使用方法HttpSession.getAttribute(name)來讀取Session中的一個變量值,如果name是一個沒有定義的變量,那麽返回的是null。需要註意的是,從getAttribute讀出的變量類型是Object,必須使用強制類型轉換,比如:String uid = (String) session.getAttribute("uid");
  (4) 關閉session,當時用完session後,可以使用session.invalidate()方法關閉session。但是這並不是嚴格要求的。因為,Servlet引擎在一段時間之後,自動關閉seesion。
HttpSession session = request.getSession(true); //參數true是指在沒有session時創建一個新的
   Date created = new Date(session.getCreationTime()); //得到session對象創建的時間
   out.println("ID " + session.getId()+"<br>"); //得到該session的id,並打印
   out.println("Created: " + created+"<br>");//打印session創建時間
   session.setAttribute("UID","12345678"); //在session中添加變量UID=12345678
   session.setAttribute("Name","Tom"); //在session中添加變量Name=Tom  10.2.4 Servlet的生命周期

跟客戶端的Applet相似,Servlet也遵循嚴格的生命周期。在每個Servlet實例的生命中有三種類型的事件,這三種事件分別對應於由Servlet引擎所喚醒的三個方法:
  1.init()。當Servlet第一次被裝載時,Servlet引擎調用這個Servlet的init()方法,只調用一次。系統保證,在init方法成功完成以前,是不會調用Servlet去處理任何請求的。
  2.service()。這是Servlet最重要的方法,是真正處理請求的地方。對於每個請求,Servlet引擎將調用Servlet的service方法,並把Servlet請求對象和Servlet響應對象作為參數傳遞給它。
  3.destroy()。這是相對於init的可選方法,當Servlet即將被卸載時由Servlet引擎來調用,這個方法用來清除並釋放在init方法中所分配的資源。
Servlet的生命周期可以被歸納為以下幾步:
  (1) 裝載Servlet,這一項操作一般是動態執行的。然而,Servlet通常會提供一個管理的選項,用於在Servlet啟動時強制裝載和初始化特定的Servlet
  (2) Server創建一個Servlet實例
  (3) Server調用Servlet的init方法
  (4) 一個客戶端請求到達Server
  (5) Server創建一個請求對象
  (6) Server創建一個響應對象
  (7) Server激活Servlet的service方法,傳遞請求和響應對象作為參數
  (8) service方法獲得關於請求對象的信息,處理請求,訪問其他資源,獲得需要的信息
  (9) service方法使用響應對象的方法。將響應傳回Server,最終到達客戶端。Service方法可能激活其他方法以處理請求。如doGet,doPost或其他程序員自己開發的方法
  (10) 對於更多的客戶端請求,Server創建新的請求和響應對象,仍然激活此servlet的service方法,將這兩個對象作為參數傳遞給它,如此重復以上的循環,但無需再次調用init方法,Servlet一般只初始化一次
  (11) 當Server不再需要Servlet時,比如當Server要關閉時,Server調用Servlet的destroy

JSP一般的運行方式為:當服務器啟動後,當Web瀏覽器端發送過來一個頁面請求時,Web服務器先判斷是否是JSP頁面請求。如果該頁面只是一般的HTML/XML頁面請求,則直接將HTML/XML頁面代碼傳給Web瀏覽器端。如果請求的頁面是JSP頁面,則由JSP引擎檢查該JSP頁面,如果該頁面是第一次被請求、或不是第一次被請求但已被修改,則JSP引擎將此JSP頁面代碼轉換成Servlet代碼,然後JSP引擎調用服務器端的Java編譯器javac.exe對Servlet代碼進行編譯,把它變成字節碼(.class)文件,然後再調用JAVA虛擬機執行該字節碼文件,然後將執行結果傳給Web瀏覽器端。如果該JSP頁面不是第一次被請求,且沒有被修改過,則直接由JSP引擎調用JAVA虛擬機執行已編譯過的字節碼.class文件,然後將結果傳送Web瀏覽器端。

采用JSP來表現頁面,采用Servlet來完成大量的處理,Servlet扮演一個控制者的角色,並負責響應客戶請求。Servlet創建JSP需要的Bean和對象,根據用戶的行為,決定將哪個JSP頁面發送給用戶。特別要註意的是,JSP頁面中沒有任何商業處理邏輯,它只是簡單的檢索Servlet先前創建的Beans或者對象,再將動態內容插入預定義的模板。

[html] view plain copy
    1. <pre name="code" class="html"></pre>
    2. <pre></pre>
    3. <pre name="code" class="html"><pre></pre>
    4. <pre></pre>
    5. <pre></pre>
    6. <pre></pre>
    7. <pre></pre>
    8. <pre></pre>
    9. </pre>

JSP與Servlet的關系理解