Servlet是一種服務器端的Java應用程序,具有獨立于平臺和協(xié)議的特性,可以生成動態(tài)的Web頁面。 它擔當客戶請求(Web瀏覽器或其他HTTP客戶程序)與服務器響應(HTTP服務器上的數(shù)據(jù)庫或應用程序)的中間層。 Servlet是位于Web 服務器內部的服務器端的Java應用程序,與傳統(tǒng)的從命令行啟動的Java應用程序不同,Servlet由Web服務器進行加載,該Web服務器必須包含 支持Servlet的Java虛擬機。
HTTPServlet應用編程接口
HTTP Servlet 使用一個 HTML 表格來發(fā)送和接收數(shù)據(jù)。要創(chuàng)建一個 HTTP Servlet,請擴展 HttpServlet 類, 該類是用專門的方法來處理 HTML 表格的 GenericServlet 的一個子類。 HTML 表單是由 <FORM> 和 </FORM> 標記定義的。表單中典型地包含輸入字段(如文本輸入字段、復選框、單選按鈕和選擇列表)和用于提交數(shù)據(jù)的按鈕。當提交信息時,它們還指定服務器應執(zhí)行哪一個Servlet(或其它的程序)。 HttpServlet 類包含 init()、destroy()、service() 等方法。其中 init() 和 destroy() 方法是繼承的。
(1) init() 方法
在 Servlet 的生命期中,僅執(zhí)行一次 init() 方法。它是在服務器裝入 Servlet 時執(zhí)行的。 可以配置服務器,以在啟動服務器或客戶機首次訪問 Servlet 時裝入 Servlet。 無論有多少客戶機訪問 Servlet,都不會重復執(zhí)行 init() 。
缺省的 init() 方法通常是符合要求的,但也可以用定制 init() 方法來覆蓋它,典型的是管理服務器端資源。 例如,可能編寫一個定制 init() 來只用于一次裝入 GIF 圖像,改進 Servlet 返回 GIF 圖像和含有多個客戶機請求的性能。另一個示例是初始化數(shù)據(jù)庫連接。缺省的 init() 方法設置了 Servlet 的初始化參數(shù),并用它的 ServletConfig 對象參數(shù)來啟動配置, 因此所有覆蓋 init() 方法的 Servlet 應調用 super.init() 以確保仍然執(zhí)行這些任務。在調用 service() 方法之前,應確保已完成了 init() 方法。
(2) service() 方法
service() 方法是 Servlet 的核心。每當一個客戶請求一個HttpServlet 對象,該對象的service() 方法就要被調用,而且傳遞給這個方法一個"請求"(ServletRequest)對象和一個"響應"(ServletResponse)對象作為參數(shù)。 在 HttpServlet 中已存在 service() 方法。缺省的服務功能是調用與 HTTP 請求的方法相應的 do 功能。例如, 如果 HTTP 請求方法為 GET,則缺省情況下就調用 doGet() 。Servlet 應該為 Servlet 支持的 HTTP 方法覆蓋 do 功能。因為 HttpServlet.service() 方法會檢查請求方法是否調用了適當?shù)奶幚矸椒?,不必要覆蓋 service() 方法。只需覆蓋相應的 do 方法就可以了。
Servlet的響應可以是下列幾種類型:
一個輸出流,瀏覽器根據(jù)它的內容類型(如text/HTML)進行解釋。
一個HTTP錯誤響應, 重定向到另一個URL、servlet、JSP。
(3)doGet()方法
當一個客戶通過HTML 表單發(fā)出一個HTTP GET請求或直接請求一個URL時,doGet()方法被調用。與GET請求相關的參數(shù)添加到URL的后面,并與這個請求一起發(fā)送。當不會修改服務器端的數(shù)據(jù)時,應該使用doGet()方法。
(4)doPost()方法
當一個客戶通過HTML 表單發(fā)出一個HTTP POST請求時,doPost()方法被調用。與POST請求相關的參數(shù)作為一個單獨的HTTP 請求從瀏覽器發(fā)送到服務器。當需要修改服務器端的數(shù)據(jù)時,應該使用doPost()方法。
(5) destroy() 方法
destroy() 方法僅執(zhí)行一次,即在服務器停止且卸裝Servlet 時執(zhí)行該方法。典型的,將 Servlet 作為服務器進程的一部分來關閉。缺省的 destroy() 方法通常是符合要求的,但也可以覆蓋它,典型的是管理服務器端資源。例如,如果 Servlet 在運行時會累計統(tǒng)計數(shù)據(jù),則可以編寫一個 destroy() 方法,該方法用于在未裝入 Servlet 時將統(tǒng)計數(shù)字保存在文件中。另一個示例是關閉數(shù)據(jù)庫連接。
當服務器卸裝 Servlet 時,將在所有 service() 方法調用完成后,或在指定的時間間隔過后調用 destroy() 方法。一個Servlet 在運行service() 方法時可能會產生其它的線程,因此請確認在調用 destroy() 方法時,這些線程已終止或完成。
(6) GetServletConfig()方法
GetServletConfig()方法返回一個 ServletConfig 對象,該對象用來返回初始化參數(shù)和ServletContext。ServletContext 接口提供有關servlet 的環(huán)境信息。
(7) GetServletInfo()方法
GetServletInfo()方法是一個可選的方法,它提供有關servlet 的信息,如作者、版本、版權。
當服務器調用sevlet 的Service()、doGet()和doPost()這三個方法時,均需要 "請求"和"響應"對象作為參數(shù)。"請求"對象提供有關請求的信息,而"響應"對象提供了一個將響應信息返回給瀏覽器的一個通信途徑。
javax.servlet 軟件包中的相關類為ServletResponse和ServletRequest,而javax.servlet.http 軟件包中的相關類為HttpServletRequest 和 HttpServletResponse。Servlet 通過這些對象與服務器通信并最終與客戶機通信。Servlet 能通過調用"請求"對象的方法獲知客戶機環(huán)境,服務器環(huán)境的信息和所有由客戶機提供的信息。Servlet 可以調用"響應"對象的方法發(fā)送響應,該響應是準備發(fā)回客戶機的。
<web-app>
......
<servlet>
<servlet-name>AnyName</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
......
<servlet-mapping>
<servlet-name>AnyName</servlet-name>
<url-pattern>/demo/hello.html</url-pattern>
</servlet-mapping>
......
</web-app>
l Servlet可以看作是嵌套了HTML代碼的Java類。
l JSP可以看作是嵌套了Java代碼的HTML頁面。
l JSP可用一種簡單易懂的等式表示為:HTML+Java=JSP。
JSP 的執(zhí)行過程
(1) 客戶端發(fā)出Request (請求);
(2) JSP Container 將JSP轉譯成Servlet的源代碼;
(3) 將產生的Servlet 的源代碼經過編譯后,并加載到內存執(zhí)行;
(4) 把結果Response (響應)至客戶端。
在執(zhí)行 JSP 網頁時,通??煞譃閮蓚€時期:轉譯時期(Translation Time)和請求時期(Request Time)
轉譯時期:JSP網頁轉譯成Servlet類。
請求時期:Servlet類執(zhí)行后,響應結果至客戶端。
注:
轉譯期間主要做了兩件事情:將JSP網頁轉譯為 Servlet 源代碼(.java),此段稱為轉譯時
期(Translation time);將Servlet源代碼(.java)編譯成 Servlet 類(.class),此段稱為
編譯時期(Compilation time)。
l 使用request.getParameter("")方法獲得提交的數(shù)據(jù)。
l response.setContentType("text/html;charset=utf-8")方法設置發(fā)送使用UTF-8編碼格式。
l request.setCharacterEncoding("utf-8")方法設置接收時使用UTF-8編碼格式。
l response.getWriter()方法返回一個打印流。
<%
String username = request.getParameter("username");
String password= request.getParameter("password");
%>
<%=username%>
<%=password%>
l JSP原始代碼中包含了JSP元素和Template data兩類。
l Template data指JSP不直接處理的部分,即<%....%>以外的部分。
l JSP元素指JSP直接處理的部分,這部分必須符合Java語法,不然會導致編譯錯誤。
JSP語法分為三種類型:
l 編譯器指令(DIRECTIVE)<%@ page import="java.io.*" %>
l 腳本語法(SCRIPTING)
l 動作語法(ACTION)<jsp:forward> <jsp:include>
l HTML注釋 <!--comments--> 客戶端可以看到
l 隱藏注釋 <%--comments--%> 不會發(fā)送到客戶端,盡量寫隱藏注釋。
l 聲明
l 表達式
l 腳本段
l 編譯器指令包括:包含指令、頁指令、taglib指令。
l 他們包含在<%@ %>卷標里。
l 兩個主要的指令是page和include。
主要的有:<jsp:forward> <jsp:include> <jsp:getProperty> <jsp:setProperty> <jsp:useBean>
<% int a%>和<%! int a%>的區(qū)別在于,加了!則變量在Java代碼中被聲明為全局變量。不加則被聲明為成員變量。
用于頁面上輸入信息。<%= %>
<% %>
include指令:向頁面插入一個靜態(tài)文件的內容。<%@ include file="相對位置" %>
l Page屬性用于定義JSP文件中的全局屬性。
l 屬性:
1. language="java" 聲明腳本語言的種類,目前只能用Java。
2. import="{package.class}"需要導入Java包的列表。這些包用于程序段,表達式以及聲明。java.lang.*、javax.servlet.*、javax.servlet.jsp.*、javax.servlet.http.*已經被默認導入。
用于引用定制標簽庫。
<%@ taglib uri="" prefix=""%>
重定向一個HTML文件,JSP文件,或者是一個程序段。<jsp:forward>標簽從一個JSP文件向另一個文件傳遞一個包含用戶請求的request對象。<jsp:forward>標簽以下的代碼,將不能執(zhí)行。 你能夠向目標文件傳送參數(shù)和值,在這個例子中我們傳遞的參數(shù)名為username,值為scott,如果你使用了<jsp:param>標簽的話,目標文件必須是一個動態(tài)的文件,能夠處理參數(shù)。
例子
<jsp:forward page="/servlet/login" />
<jsp:forward page="/servlet/login">
<jsp:param name="username" value="jsmith" />
</jsp:forward>
l <jsp:include>元素允許你包含動態(tài)文件和靜態(tài)文件,這兩種包含文件的結果是不同的。如果文件僅是靜態(tài)文件,那么這種包含僅僅是把包含文件的內容加到jsp文件中去,而如果這個文件是動態(tài)的,那么這個被包含文件也會被Jsp編譯器執(zhí)行(這一切與asp相似)
l 你不能從文件名上判斷一個文件是動態(tài)的還是靜態(tài)的,比如aspcn.asp 就有可能只是包含一些信息而已,而不需要執(zhí)行。<jsp:include>能夠同時處理這兩種文件,因此你就不需要包含時還要判斷此文件是動態(tài)的還是靜態(tài)的。如果這個包含文件是動態(tài)的,那么你還可以用<jsp:param>還傳遞參數(shù)名和參數(shù)值。
JSP中include指令和include動作區(qū)別
l <%@ include file=”***.jsp”%> 是把整個外部文件加載進來,把整個外部文件和原文件轉化成一個servlet。
l <jsp:include page=”***.jsp” flush=”true”/> include動作元素引入頁面時,實際只是引用了***.jsp這個文件被轉化并被編譯后產生的servlet類文件。既如此,***.jsp就是作為一個單獨的文件在執(zhí)行后才被test.jsp文件運行時調用。
jsp內置對象
定義:可以不加聲明就在JSP頁面腳本(Java程序片和Java表達式)中使用的成員變量 JSP共有以下9種基本內置組件(可與ASP的6種內部組件相對應):
內置對象 | 代表內容 | 范圍 |
request | 觸發(fā)服務調用的請求 | request |
response | 對請求的應答 | page |
session | 為請求的客戶創(chuàng)建的session對象 | session |
application | 從 servlet配置對象獲得的 servlet上下文(如在getServletConfig(),getContext()的調用中) | application |
out | 向輸出流寫入內容的對象 | page |
pageContext | 本 JSP的頁面上下文 | page |
page | 實現(xiàn)處理本頁當前請求的類的實例 | page |
config | 本 JSP的 ServletConfig | page |
exception | 表示JSP頁面運行時產生的異常 | Page |
l Request對象代表來自客戶端的請求。使用較多的方法有getParameter、getParameterNames、getParameterValues,通過調用這幾個方法來獲取請求對象中所包含的參數(shù)的值。
l Response對象代表對客戶端的響應。由于比較底層不建議使用。發(fā)送文字時直接使用out對象。
為上下文對象,代表當前頁面運行的一些屬性。常用方法有findAttribute、getAttribute、getAttributesScope。較少使用此對象。
l Session對象非常重要,代表服務器與客戶端所建立的對話,當需要在不同頁面保留客戶信息的情況下使用。如在線購物,客戶軌跡跟蹤等。
l 概要:
— HTTP是無狀態(tài)協(xié)議。
— Web Server 對每個客戶端請求都沒有歷史記憶。
— Session用來保護客戶端信息。
Cookie包含一對鍵值對。下面代碼生成一個Cookie并寫入用戶硬盤。
Cookie myCookie = new Cookie("key","value");
response.addCookie(myCookie );
l 有許多相似之處,可以生成動態(tài)網頁。
l JSP的優(yōu)點是善于網頁制作,生成動態(tài)頁面比較直觀。JSP的缺點是不容易跟蹤和排錯。
l Servlet是純Java語言,擅長處理流程和業(yè)務邏輯。Servlet缺點是生成動態(tài)網頁不直觀。
l request.setAttribute("test","hello");
l request.getRequestDispatcher("/test.jsp").forward(request,response);這個方法為請求轉發(fā),可以傳送test屬性。
l response.sendRedirect("/test.jsp");這個方法為重定向,不能傳送test屬性,說明不共享用一個request。
請求轉發(fā)和重定向原理圖:
l 跟蹤客戶狀態(tài)
— Web服務器跟蹤客戶狀態(tài)通常有四種方法
— 建立含有跟蹤數(shù)據(jù)的隱藏字段
— 重寫包含額外參數(shù)的URL
— 使用持續(xù)的COOKIE
— 使用Servlet API中的Session(會話)機制
l 隱藏字段
— <inputtype="hidden"name="name"value="value" >此代碼不會顯示在頁面上。
l 使用Session會話機制
— Session用于跟蹤客戶狀態(tài)。Session指的是在一段時間內,單個客戶與web服務器的一串相關的交互過程。在一個Session中用戶可能會多次請求訪問同一個網頁,也有可能請求訪問不同的服務器資源。
— 當一個Session開始時,Servlet容器創(chuàng)建一個HttpSession對象,在此對象中可以存放用戶信息。
— Servlet容易為HttpSession分配一個唯一的標識符,稱為Session ID。Servlet容易把標識符作為COOKIE保存在客戶瀏覽器中。
— 每次客戶發(fā)送HTTP請求,Servlet容器可以從HttpServletRequest對象中讀取Session ID,然后根據(jù)Session ID找到相應的HttpSession對象,從而獲取用戶的狀態(tài)信息。
l Session生命周期
— 當用戶第一次訪問一個支持Session的web應用的時候,將開始一個新的Session。
— 在這個用戶瀏覽這個web應用不同的網頁的時候,一直處于同一個Session中。
— 默認情況下,JSP都是支持Session的,也可以通過以下語句顯示聲明Session。<%@ page session="true"%>
— 結束Session生命周期:1、Session過期。2、服務端調用HttpSession的invalidate()方法。3、關閉瀏覽器。
l 關閉瀏覽器Session真的就消失了嗎?
— HttpSession會把Session ID保存在COOKIE中。
— 這個COOKIE不同于普通的COOKIE,COOKIE有兩種類型,一個是我們通常理解的存放在客戶端硬盤上的COOKIE,這里的類型是會話Session,實際是存儲在瀏覽器進程的內存當中的,所以關閉瀏覽器也就意味著瀏覽器進程的內存得到釋放,從而COOKIE消失。
— 對于服務器端,我們需要設置Session的最大存活時間,使用戶沒有在一段時間和服務器交互,Session會自動失效,我們使用HttpSession的setMaxInactiveInterval()方法來設置時間,此時間以秒為單位。不然服務器永不關閉,會無端的消耗服務器的內存資源。
l 觀察者模式:觀察者模式(Observer)完美的將觀察者和被觀察的對象分離開。舉個例子,用戶界面可以作為一個觀察者,業(yè)務數(shù)據(jù)是被觀察者,用戶界面觀察業(yè)務數(shù)據(jù) 的變化,發(fā)現(xiàn)數(shù)據(jù)變化后,就顯示在界面上。面向對象設計的一個原則是:系統(tǒng)中的每個類將重點放在某一個功能上,而不是其他方面。一個對象只做一件事情,并 且將他做好。觀察者模式在模塊之間劃定了清晰的界限,提高了應用程序的可維護性和重用性。
l 監(jiān)聽器就是一個實現(xiàn)特定接口的普通java程序,這個程序專門用于監(jiān)聽另一個java對象的方法調用或屬性改變,當被監(jiān)聽對象發(fā)生上述事件后,監(jiān)聽器某個方法將立即被執(zhí)行。
l 在Servlet規(guī)范中定義了多種類型的監(jiān)聽器,它們用于監(jiān)聽的事件源分別為 ServletContext, HttpSession 和 ServletRequest 這三個域對象。
l Servlet規(guī)范針對這三個對象上的操作,又把這多種類型的監(jiān)聽器劃分為三種類型。
— 監(jiān)聽三個域對象創(chuàng)建和銷毀的事件監(jiān)聽器
— 監(jiān)聽域對象中屬性的增加和刪除的事件監(jiān)聽器
— 監(jiān)聽綁定到 HttpSession 域中的某個對象的狀態(tài)的事件監(jiān)聽器。(查看API文檔)
l 編寫和配置監(jiān)聽器
— 和編寫其它事件監(jiān)聽器一樣,編寫servlet監(jiān)聽器也需要實現(xiàn)一個特定的接口,并針對相應動作覆蓋接口中的相應方法。
— 和其它事件監(jiān)聽器略有不同的是,servlet監(jiān)聽器的注冊不是直接注冊在事件源上,而是由WEB容器負責注冊,開發(fā)人員只需在web.xml文件中使用<listener>標簽配置好監(jiān)聽器,web容器就會自動把監(jiān)聽器注冊到事件源中。
— 一個 web.xml 文件中可以配置多個 Servlet 事件監(jiān)聽器,web 服務器按照它們在 web.xml 文件中的注冊順序來加載和注冊這些 Serlvet 事件監(jiān)聽器。
l 監(jiān)聽servletContext域對象創(chuàng)建和銷毀
— ServletContextListener 接口用于監(jiān)聽 ServletContext 對象的創(chuàng)建和銷毀事件。
— 當 ServletContext 對象被創(chuàng)建時,激發(fā)contextInitialized (ServletContextEvent sce)方法
— 當 ServletContext 對象被銷毀時,激發(fā)contextDestroyed(ServletContextEvent sce)方法。
— 提問,servletContext域對象何時創(chuàng)建和銷毀:
u 創(chuàng)建:服務器啟動針對每一個web應用創(chuàng)建servletContext
u 銷毀:服務器關閉前先關閉代表每一個web應用的servletContext
l 監(jiān)聽HttpSession域對象創(chuàng)建和銷毀
— HttpSessionListener接口用于監(jiān)聽HttpSession的創(chuàng)建和銷毀
— 創(chuàng)建一個Session時,sessionCreated(HttpSessionEvent se) 方法將會被調用。
— 銷毀一個Session時,sessionDestroyed (HttpSessionEvent se) 方法將會被調用。
— Session域對象創(chuàng)建和銷毀的時機
u 創(chuàng)建:用戶每一次訪問時,服務器創(chuàng)建session
u 銷毀:如果用戶的session 30分鐘沒有使用,服務器就會銷毀session,我們在web.xml里面也可以配置session失效時間
l 監(jiān)聽HttpRequest域對象創(chuàng)建和銷毀
— ServletRequestListener 接口用于監(jiān)聽ServletRequest 對象的創(chuàng)建和銷毀。
— Request 對象被創(chuàng)建時,監(jiān)聽器的requestInitialized方法將會被調用。
— Request對象被銷毀時,監(jiān)聽器的requestDestroyed方法將會被調用。
— servletRequest域對象創(chuàng)建和銷毀的時機:
u 創(chuàng)建:用戶每一次訪問,都會創(chuàng)建一個reqeust
u 銷毀:當前訪問結束,request對象就會銷毀
l 監(jiān)聽三個域對象屬性變化
— Servlet規(guī)范定義了監(jiān)聽 ServletContext, HttpSession, HttpServletRequest 這三個對象中的屬性變更信息事件的監(jiān)聽器。
— 這三個監(jiān)聽器接口分別是ServletContextAttributeListener, HttpSessionAttributeListener ServletRequestAttributeListener
— 這三個接口中都定義了三個方法來處理被監(jiān)聽對象中的屬性的增加,刪除和替換的事件,同一個事件在這三個接口中對應的方法名稱完全相同,只是接受的參數(shù)類型不同。
— attributeAdded 方法
— attributeRemoved 方法
— attributeReplaced 方法
l 編寫一個實現(xiàn)Tag接口的Java類,并覆蓋doStartTag方法,把jsp頁面中的java代碼寫到doStartTag方法中。
l 編寫標簽庫描述符(tld)文件,在tld文件中對自定義標簽進行描述。
l 完成以上操作,即可在JSP頁面中導入和使用自定義標簽。
最重要的兩個方法:public int doStartTag()和public int doEndTag()。
l Servlet API中提供了一個Filter接口,開發(fā)web應用時,如果編寫的Java類實現(xiàn)了這個接口,則把這個java類稱之為過濾器Filter。通過Filter技術,開發(fā)人員可以實現(xiàn)用戶在訪問某個目標資源之前,對訪問的請求和響應進行攔截,如下所示:
l Filter開發(fā)分為二個步驟:
— 編寫java類實現(xiàn)Filter接口,并實現(xiàn)其doFilter方法。
— 在 web.xml 文件中使用<filter>和<filter-mapping>元素對編寫的filter類進行注冊,并設置它所能攔截的資源。(動手實驗)
l Filter鏈
— 在一個web應用中,可以開發(fā)編寫多個Filter,這些Filter組合起來稱之為一個Filter鏈。
— web服務器根據(jù)Filter在web.xml文件中的注冊順序,決定先調用哪個Filter,當?shù)谝粋€Filter的doFilter方法被調用時,web服務器會創(chuàng)建一個代表Filter鏈的FilterChain對象傳遞給該方法。在doFilter方法中,開發(fā)人員如果調用了FilterChain對象的doFilter方法,則web服務器會檢查FilterChain對象中是否還有filter,如果有,則調用第2個filter,如果沒有,則調用目標資源。