Servlet
Servlet 為創(chuàng)建基于 web 的應用程序提供了基于組件胧奔、獨立于平臺的方法,可以不受 CGI 程序的性能限制邢笙。Servlet 有權限訪問所有的 Java API,包括訪問企業(yè)級數(shù)據(jù)庫的 JDBC API奇颠。
Servlet 是什么民珍?
Java Servlet 是運行在Web服務器或應用服務器上的程序襟士,它是作為來自Web瀏覽器或其他HTTP客戶端的請求和HTTP服務器上的數(shù)據(jù)庫或應用程序之間的中間層。
使用Servlet嚷量,可以收集來自網(wǎng)頁表單的用戶輸入陋桂,呈現(xiàn)來自數(shù)據(jù)庫或者其他源的記錄,還可以動態(tài)創(chuàng)建網(wǎng)頁蝶溶。
Java Servlet通常情況下雨使用CGI(Common Gateway Interface, 公共網(wǎng)關接口)
但是相比于 CGI嗜历,Servlet 有以下幾點優(yōu)勢:
- 性能明顯更好。
- Servlet 在 Web 服務器的地址空間內執(zhí)行抖所。這樣它就沒有必要再創(chuàng)建一個單獨的進程來處理每個客戶端請求梨州。
- Servlet 是獨立于平臺的,因為它們是用 Java 編寫的田轧。
- 服務器上的 Java 安全管理器執(zhí)行了一系列限制暴匠,以保護服務器計算機上的資源。因此傻粘,Servlet 是可信的巷查。
- Java 類庫的全部功能對 Servlet 來說都是可用的。它可以通過 sockets 和 RMI 機制與 applets抹腿、數(shù)據(jù)庫或其他軟件進行交互岛请。
Servlet 架構
Servlet 任務
Servlet 執(zhí)行以下主要任務:
- 讀取客戶端(瀏覽器)發(fā)送的顯式的數(shù)據(jù)。這包括網(wǎng)頁上的 HTML 表單警绩,或者也可以是來自 applet 或自定義的 HTTP 客戶端程序的表單崇败。
- 讀取客戶端(瀏覽器)發(fā)送的隱式的 HTTP 請求數(shù)據(jù)。這包括 cookies肩祥、媒體類型和瀏覽器能理解的壓縮格式等等后室。
- 處理數(shù)據(jù)并生成結果。這個過程可能需要訪問數(shù)據(jù)庫混狠,執(zhí)行 RMI 或 CORBA 調用岸霹,調用 Web 服務,或者直接計算得出對應的響應将饺。
- 發(fā)送顯式的數(shù)據(jù)(即文檔)到客戶端(瀏覽器)贡避。該文檔的格式可以是多種多樣的痛黎,包括文本文件(HTML 或 XML)、二進制文件(GIF 圖像)刮吧、Excel 等湖饱。
- 發(fā)送隱式的 HTTP 響應到客戶端(瀏覽器)。這包括告訴瀏覽器或其他客戶端被返回的文檔類型(例如 HTML)杀捻,設置 cookies 和緩存參數(shù)井厌,以及其他類似的任務。
Servlet 生命周期
Servlet 生命周期可被定義為從創(chuàng)建直到毀滅的整個過程致讥。以下是 Servlet 遵循的過程:
- Servlet 通過調用 init () 方法進行初始化仅仆。
- Servlet 調用 service() 方法來處理客戶端的請求。
- Servlet 通過調用 destroy() 方法終止(結束)垢袱。
- 最后蝇恶,Servlet 是由 JVM 的垃圾回收器進行垃圾回收的。
init() 方法
init 方法被設計成只調用一次惶桐。它在第一次創(chuàng)建 Servlet 時被調用撮弧,在后續(xù)每次用戶請求時不再調用。
當用戶調用一個 Servlet 時姚糊,就會創(chuàng)建一個 Servlet 實例贿衍,每一個用戶請求都會產(chǎn)生一個新的線程,適當?shù)臅r候移交給 doGet 或 doPost 方法救恨。init() 方法簡單地創(chuàng)建或加載一些數(shù)據(jù)贸辈,這些數(shù)據(jù)將被用于 Servlet 的整個生命周期。
public void init() throws ServletException {
// 初始化代碼...
}
service() 方法
service() 方法是執(zhí)行實際任務的主要方法肠槽。Servlet 容器(即 Web 服務器)調用 service() 方法來處理來自客戶端(瀏覽器)的請求擎淤,并把格式化的響應寫回給客戶端。
每次服務器接收到一個 Servlet 請求時秸仙,服務器會產(chǎn)生一個新的線程并調用服務嘴拢。service() 方法檢查 HTTP 請求類型(GET、POST寂纪、PUT席吴、DELETE 等),并在適當?shù)臅r候調用 doGet捞蛋、doPost孝冒、doPut,doDelete 等方法拟杉。
public void service(ServletRequest request,
ServletResponse response)
throws ServletException, IOException{
}
service() 方法由容器調用庄涡,service 方法在適當?shù)臅r候調用 doGet、doPost搬设、doPut穴店、doDelete 等方法撕捍。所以,您不用對 service() 方法做任何動作迹鹅,您只需要根據(jù)來自客戶端的請求類型來重寫 doGet() 或 doPost() 即可卦洽。
doGet() 和 doPost() 方法是每次服務請求中最常用的方法贞言。下面是這兩種方法的特征斜棚。
doGet() 方法
GET 請求來自于一個 URL 的正常請求,或者來自于一個未指定 METHOD 的 HTML 表單该窗,它由 doGet() 方法處理弟蚀。
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代碼
}
doPost() 方法
POST 請求來自于一個特別指定了 METHOD 為 POST 的 HTML 表單,它由 doPost() 方法處理酗失。
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代碼
}
destroy() 方法
destroy() 方法只會被調用一次义钉,在 Servlet 生命周期結束時被調用。destroy() 方法可以讓您的 Servlet 關閉數(shù)據(jù)庫連接规肴、停止后臺線程捶闸、把 Cookie 列表或點擊計數(shù)器寫入到磁盤,并執(zhí)行其他類似的清理活動拖刃。
在調用 destroy() 方法之后删壮,servlet 對象被標記為垃圾回收。destroy 方法定義如下所示:
public void destroy() {
// 終止化代碼...
}
架構圖
下圖顯示了一個典型的 Servlet 生命周期方案兑牡。
第一個到達服務器的 HTTP 請求被委派到 Servlet 容器央碟。
Servlet 容器在調用 service() 方法之前加載 Servlet。
然后 Servlet 容器處理由多個線程產(chǎn)生的多個請求均函,每個線程執(zhí)行一個單一的 Servlet 實例的 service() 方法亿虽。
HelloServlet
// 導入必需的 java 庫
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
// 擴展 HttpServlet 類
public class HelloWorld extends HttpServlet {
private String message;
public void init() throws ServletException
{
// 執(zhí)行必需的初始化
message = "Hello World";
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 設置響應內容類型
response.setContentType("text/html");
// 實際的邏輯是在這里
PrintWriter out = response.getWriter();
out.println("<h1>" + message + "</h1>");
}
public void destroy()
{
// 什么也不做
}
}
Servlet 部署
在web.xml 文件中創(chuàng)建以下條目:
<web-app>
<!--注冊servlet-->
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>HelloWorld</servlet-class>
</servlet>
<!--servlet的請求路徑-->
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
</web-app>
mapping問題
-
一個servlet可以指定一個映射路徑
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一個servlet可以指定多個映射路徑
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello1</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping>
-
一個servlet可以指定通用映射路徑
<!--默認請求路徑--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <!--可以自定義后綴實現(xiàn)在請求映射 注意: *前不能加映射的路徑/--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.susu</url-pattern> </servlet-mapping>
ServletContext
ServletContext官方叫servlet上下文。服務器會為每一個工程創(chuàng)建一個對象苞也,這個對象就是ServletContext對象洛勉。這個對象全局唯一,而且工程內部的所有servlet都共享這個對象如迟。所以叫全局應用程序共享對象坯认。
- 數(shù)據(jù)共享:
- 在一個servlet中保存的數(shù)據(jù),可以在另外一個servlet中拿到
ServletContext servletContext = this.getServletContext(); String username = "qinjiang"; servletContext.setAttribute("username",username); //將數(shù)據(jù)保存在了ServletContext中氓涣, resp.setContentType("text/html;charset=UTF-8"); resp.setCharacterEncoding("UTF-8"); ServletContext context = this.getServletContext(); String username = (String) context.getAttribute("username"); resp.getWriter().println("請將"+username);
- 獲取初始化參數(shù)
<context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/mysql</param-value> </context-param>
ServletContext context = this.getServletCOntext(); String url = context.getInitParameter("url"); resp.getWriter().print(url);
- 請求轉發(fā)
ServletContext context = this.getServletCOntext(); RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); // 轉發(fā)請求的路徑 requestDispatcher.forward(req,resp); //調用forward實現(xiàn)請求轉發(fā)
- 讀取資源文件
InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties"); //注意路徑牛哺,應該為編譯后的路徑 Properties properties = new Properties(); properties.load(resourceAsStream); String username = properties.getProperty("username"); String password = properties.getProperty("password"); resp.getWriter().println(username+"df wse "+password);
HttpServletResponse
web服務器接收到客戶端的http請求,針對這個請求劳吠,分別創(chuàng)建一個代表請求的HttpServletRequest對象引润,代表相應的一個HttpServletResponse;
- 如果要獲取客戶端請求過來的參數(shù):找HttpServletRequest
- 如果要給客戶端相應一些信息: 找HttpServletResponse
HttpServletRequest
Servlet異常處理
當一個servlet跑出一個異常時痒玩,web容器在是用來exception-type元素的web.xml中搜索與拋出異常類型相匹配的配置淳附。
所以必須在web.xml中使用error-page元素來指定對特定異骋槲浚或HTTP狀態(tài)碼做出相應的Servlet調用。
web.xml 配置
假設奴曙,有一個 ErrorHandler 的 Servlet 在任何已定義的異潮鸢迹或錯誤出現(xiàn)時被調用。以下將是在 web.xml 中創(chuàng)建的項洽糟。
<!-- servlet 定義 -->
<servlet>
<servlet-name>ErrorHandler</servlet-name>
<servlet-class>ErrorHandler</servlet-class>
</servlet>
<!-- servlet 映射 -->
<servlet-mapping>
<servlet-name>ErrorHandler</servlet-name>
<url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>
<!-- error-code 相關的錯誤頁面 -->
<error-page>
<error-code>404</error-code>
<location>/ErrorHandler</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/ErrorHandler</location>
</error-page>
<!-- exception-type 相關的錯誤頁面 -->
<error-page>
<exception-type>
javax.servlet.ServletException
</exception-type >
<location>/ErrorHandler</location>
</error-page>
<error-page>
<exception-type>java.io.IOException</exception-type >
<location>/ErrorHandler</location>
</error-page>
<!--如果想對所有的異常有一個通用的錯誤處理程序炉菲,那么應該定義下面的 error-page,而不是為每個異常定義單獨的 error-page 元素:-->
<error-page>
<exception-type>java.lang.Throwable</exception-type >
<location>/ErrorHandler</location>
</error-page>
以下是關于上面的 web.xml 異常處理要注意的點:
- Servlet ErrorHandler 與其他的 Servlet 的定義方式一樣坤溃,且在 web.xml 中進行配置拍霜。
- 如果有錯誤狀態(tài)代碼出現(xiàn),不管為 404(Not Found 未找到)或 403(Forbidden 禁止)薪介,則會調用 ErrorHandler 的 Servlet祠饺。
- 如果 Web 應用程序拋出 ServletException 或 IOException,那么 Web 容器會調用 ErrorHandler 的 Servlet汁政。
- 可以定義不同的錯誤處理程序來處理不同類型的錯誤或異常道偷。上面的實例是非常通用的。
請求屬性 - 錯誤/異常
以下是錯誤處理的 Servlet 可以訪問的請求屬性列表记劈,用來分析錯誤/異常的性質勺鸦。
序號 | 屬性 & 描述 |
---|---|
1 | javax.servlet.error.status_code 該屬性給出狀態(tài)碼,狀態(tài)碼可被存儲抠蚣,并在存儲為 java.lang.Integer 數(shù)據(jù)類型后可被分析祝旷。 |
2 | javax.servlet.error.exception_type該屬性給出異常類型的信息,異常類型可被存儲嘶窄,并在存儲為 java.lang.Class 數(shù)據(jù)類型后可被分析怀跛。 |
3 | javax.servlet.error.message該屬性給出確切錯誤消息的信息,信息可被存儲柄冲,并在存儲為 java.lang.String 數(shù)據(jù)類型后可被分析吻谋。 |
4 | javax.servlet.error.request_uri該屬性給出有關 URL 調用 Servlet 的信息,信息可被存儲现横,并在存儲為 java.lang.String 數(shù)據(jù)類型后可被分析漓拾。 |
5 | javax.servlet.error.exception該屬性給出異常產(chǎn)生的信息,信息可被存儲戒祠,并在存儲為 java.lang.Throwable 數(shù)據(jù)類型后可被分析骇两。 |
6 | javax.servlet.error.servlet_name該屬性給出 Servlet 的名稱,名稱可被存儲姜盈,并在存儲為 java.lang.String 數(shù)據(jù)類型后可被分析低千。 |
Servlet Cookie處理
Cookie 是存儲在客戶端計算機上的文本文件,并保留了各種跟蹤信息馏颂。
識別返回用戶包括三個步驟:
- 服務器腳本向瀏覽器發(fā)送一組 Cookie示血。例如:姓名棋傍、年齡或識別號碼等。
- 瀏覽器將這些信息存儲在本地計算機上难审,以備將來使用瘫拣。
- 當下一次瀏覽器向 Web 服務器發(fā)送任何請求時,瀏覽器會把這些 Cookie 信息發(fā)送到服務器告喊,服務器將使用這些信息來識別用戶麸拄。
Servlet Cookie 處理需要對中文進行編碼與解碼,方法如下:
String str = java.net.URLEncoder.encode("中文"葱绒,"UTF-8"); //編碼
String str = java.net.URLDecoder.decode("編碼后的字符串","UTF-8"); // 解碼
Cookie 剖析
Cookie 通常設置在 HTTP 頭信息中感帅。設置 Cookie 的 Servlet 會發(fā)送如下的頭信息:
HTTP/1.1 200 OK
Date: Fri, 04 Feb 2000 21:03:38 GMT
Server: Apache/1.3.9 (UNIX) PHP/4.0b3
Set-Cookie: name=xyz; expires=Friday, 04-Feb-07 22:03:38 GMT;
path=/; domain=runoob.com
Connection: close
Content-Type: text/html
Set-Cookie 頭包含了一個名稱值對斗锭、一個 GMT 日期地淀、一個路徑和一個域。名稱和值會被 URL 編碼岖是。expires 字段是一個指令帮毁,告訴瀏覽器在給定的時間和日期之后"忘記"該 Cookie。
如果瀏覽器被配置為存儲Cookie豺撑,它將會保留此信息直到到期日期烈疚。如果用戶的瀏覽器指向任何匹配該Cookie的路徑和域的頁面,它會重新發(fā)送Cookie到服務器聪轿。瀏覽器的頭信息可能如下所示:
GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)
Host: zink.demon.co.uk:1126
Accept: image/gif, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Cookie: name=xyz
Servlet 就能夠通過請求方法 request.getCookies() 訪問 Cookie爷肝,該方法將返回一個 Cookie 對象的數(shù)組。
通過 Servlet 設置 Cookie
通過 Servlet 設置 Cookie 包括三個步驟:
(1) 創(chuàng)建一個 Cookie 對象:您可以調用帶有 cookie 名稱和 cookie 值的 Cookie 構造函數(shù)陆错,cookie 名稱和 cookie 值都是字符串灯抛。
Cookie cookie = new Cookie("key","value");
請記住,無論是名字還是值音瓷,都不應該包含空格或以下任何字符:
[ ] ( ) = , " / ? @ : ;
(2) 設置最大生存周期:您可以使用 setMaxAge 方法來指定 cookie 能夠保持有效的時間(以秒為單位)。下面將設置一個最長有效期為 24 小時的 cookie。
cookie.setMaxAge(60*60*24);
可以使用 setMaxAge() 方法設置 cookie 的年齡為零鹅颊,來刪除現(xiàn)有的 cookie彩匕。
(3) 發(fā)送 Cookie 到 HTTP 響應頭:您可以使用 response.addCookie 來添加 HTTP 響應頭中的 Cookie,如下所示:
response.addCookie(cookie);
Servlet Session 跟蹤
HTTP 是一種"無狀態(tài)"協(xié)議杏愤,這意味著每次客戶端檢索網(wǎng)頁時靡砌,客戶端打開一個單獨的連接到 Web 服務器,服務器會自動不保留之前客戶端請求的任何記錄珊楼。
但是仍然有以下三種方式來維持 Web 客戶端和 Web 服務器之間的 session 會話:
- Cookies
- 一個 Web 服務器可以分配一個唯一的 session 會話 ID 作為每個 Web 客戶端的 cookie通殃,對于客戶端的后續(xù)請求可以使用接收到的 cookie 來識別。
- 這可能不是一個有效的方法亥曹,因為很多瀏覽器不支持 cookie邓了,所以建議不要使用這種方式來維持 session 會話恨诱。
- 隱藏的表單字段
- 個 Web 服務器可以發(fā)送一個隱藏的 HTML 表單字段,以及一個唯一的 session 會話 ID骗炉,如下所示:
<input type="hidden" name="sessionid" value="12345">
- 該條目意味著照宝,當表單被提交時,指定的名稱和值會被自動包含在 GET 或 POST 數(shù)據(jù)中句葵。每次當 Web 瀏覽器發(fā)送回請求時厕鹃,session_id 值可以用于保持不同的 Web 瀏覽器的跟蹤。
- 這可能是一種保持 session 會話跟蹤的有效方式乍丈,但是點擊常規(guī)的超文本鏈接(< A HREF...>)不會導致表單提交剂碴,因此隱藏的表單字段也不支持常規(guī)的 session 會話跟蹤。
- URL 重寫
- 可以在每個 URL 末尾追加一些額外的數(shù)據(jù)來標識 session 會話轻专,服務器會把該 session 會話標識符與已存儲的有關 session 會話的數(shù)據(jù)相關聯(lián)忆矛。
- 例如,http://w3cschool.cc/file.htm;sessionid=12345请垛,
session 會話標識符被附加為 sessionid=12345催训,標識符可被 Web 服務器訪問以識別客戶端。 - URL 重寫是一種更好的維持 session 會話的方式宗收,它在瀏覽器不支持 cookie 時能夠很好地工作漫拭,但是它的缺點是會動態(tài)生成每個 URL 來為頁面分配一個 session 會話 ID,即使是在很簡單的靜態(tài) HTML 頁面中也會如此混稽。
HttpSession 對象
除了上述的三種方式采驻,Servlet 還提供了 HttpSession 接口,該接口提供了一種跨多個頁面請求或訪問網(wǎng)站時識別用戶以及存儲有關用戶信息的方式匈勋。
Servlet 容器使用這個接口來創(chuàng)建一個 HTTP 客戶端和 HTTP 服務器之間的 session 會話礼旅。會話持續(xù)一個指定的時間段,跨多個連接或頁面請求颓影。
可以通過調用 HttpServletRequest 的公共方法 getSession() 來獲取 HttpSession 對象各淀,如下所示:
HttpSession session = request.getSession();
你需要在向客戶端發(fā)送任何文檔內容之前調用 request.getSession()。
Session和Cookie的區(qū)別:
- Cookie是把用戶的數(shù)據(jù)寫給用戶的瀏覽器诡挂,瀏覽器保存(可保存多個)
- Session把用戶的數(shù)據(jù)寫到用戶獨占Session中碎浇,服務器端保存(保存重要的信息,減少服務器資源的浪費)
- Session由服務器創(chuàng)建
使用場景
- 保存一個登陸用戶的信息
- 購物車信息
- 在整個網(wǎng)站中經(jīng)常使用的數(shù)據(jù)璃俗,我們將他保存在Session中
刪除 Session 會話數(shù)據(jù)
- 移除一個特定的屬性:您可以調用 public void removeAttribute(String name) 方法來刪除與特定的鍵相關聯(lián)的值奴璃。
- 刪除整個 session 會話:您可以調用 public void invalidate() 方法來丟棄整個 session 會話。
- 設置 session 會話過期時間:您可以調用 public void setMaxInactiveInterval(int interval) 方法來單獨設置 session 會話超時城豁。
- 注銷用戶:如果使用的是支持 servlet 2.4 的服務器苟穆,您可以調用 logout 來注銷 Web 服務器的客戶端,并把屬于所有用戶的所有 session 會話設置為無效。
-
web.xml 配置:如果您使用的是 Tomcat雳旅,除了上述方法跟磨,您還可以在 web.xml 文件中配置 session 會話超時,如下所示:
<session-config> <!--以分鐘為單位攒盈,將覆蓋 Tomcat 中默認的 30 分鐘超時時間抵拘。--> <session-timeout>15</session-timeout> </session-config>
Filter
Servlet 過濾器可以動態(tài)地攔截請求和響應,以變換或使用包含在請求或響應中的信息型豁。
可以將一個或多個 Servlet 過濾器附加到一個 Servlet 或一組 Servlet僵蛛。Servlet 過濾器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 頁面。調用 Servlet 前調用所有附加的 Servlet 過濾器迎变。
Servlet 過濾器是可用于 Servlet 編程的 Java 類充尉,可以實現(xiàn)以下目的:
- 在客戶端的請求訪問后端資源之前,攔截這些請求衣形。
- 在服務器的響應發(fā)送回客戶端之前驼侠,處理這些響應。
實現(xiàn)Filter接口
//導入必需的 java 庫
import javax.servlet.*;
import java.util.*;
//實現(xiàn) Filter 類
public class LogFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
// 獲取初始化參數(shù)
String site = config.getInitParameter("Site");
// 輸出初始化參數(shù)
System.out.println("網(wǎng)站名稱: " + site);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
// 輸出站點名稱
System.out.println("站點網(wǎng)址:http://www.runoob.com");
// 把請求傳回過濾鏈
chain.doFilter(request,response);
}
public void destroy( ){
/* 在 Filter 實例被 Web 容器從服務移除之前調用 */
}
}
配置web.xml
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>com.runoob.test.LogFilter</filter-class>
<!--為過濾器指定初始化參數(shù)泵喘,-->
<init-param>
<!--指定參數(shù)的名字-->
<param-name>Site</param-name>
<!--指定參數(shù)的值泪电。-->
<param-value>菜鳥教程</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LogFilter</filter-name>
<!--設置 filter 所攔截的請求路徑(過濾器關聯(lián)的URL樣式)-->
<url-pattern>/* </url-pattern>
</filter-mapping>
過濾器的應用順序
web.xml 中的 filter-mapping 元素的順序決定了 Web 容器應用過濾器到 Servlet 的順序般妙。若要反轉過濾器的順序纪铺,只需要在 web.xml 文件中反轉 filter-mapping 元素即可。
監(jiān)聽器
實現(xiàn)監(jiān)聽器接口
public class onlineCountListener implements HttpSessionListener {
//創(chuàng)建session監(jiān)聽:看你的一舉一動
//一旦創(chuàng)建session就會觸發(fā)一次這個事件
@Override
public void sessionCreated(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer onlineCount = (Integer) servletContext.getAttribute("OnlineCount");
if (onlineCount ==null){
onlineCount = new Integer(1);
}else{
int count = onlineCount.intValue();
onlineCount = new Integer(count+1);
}
servletContext.setAttribute("OnlineCount",onlineCount);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer onlineCount = (Integer) servletContext.getAttribute("OnlineCount");
if (onlineCount ==null){
onlineCount = new Integer(0);
}else{
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}
servletContext.setAttribute("OnlineCount",onlineCount);
}
}
配置web.xml
<listener>
<listener-class>com.susu.Listener.onlineCountListener</listener-class>
</listener>
Servlet 數(shù)據(jù)庫訪問
通過JDBC連接數(shù)據(jù)庫
Junit單元測試
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
使用注解
@方法名 只有在方法上有效碟渺,只要加了這個注解的方法鲜锚,就可以直接運行
網(wǎng)頁重定向
當文檔移動到新的位置,我們需要向客戶端發(fā)送這個新位置時苫拍,我們需要用到網(wǎng)頁重定向芜繁。當然,也可能是為了負載均衡绒极,或者只是為了簡單的隨機骏令,這些情況都有可能用到網(wǎng)頁重定向。
重定向請求到另一個網(wǎng)頁的最簡單的方式是使用 response 對象的 sendRedirect() 方法垄提。
public void HttpServletResponse.sendRedirect(String location)
throws IOException
該方法把響應連同狀態(tài)碼和新的網(wǎng)頁位置發(fā)送回瀏覽器榔袋。也可以通過把 setStatus() 和 setHeader() 方法一起使用來達到同樣的效果:
String site = "http://www.runoob.com" ;
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site);
實例:
@WebServlet("/PageRedirect")
public class PageRedirect extends HttpServlet{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// 設置響應內容類型
response.setContentType("text/html;charset=UTF-8");
// 要重定向的新位置
String site = new String("http://www.runoob.com");
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site);
}
}
訪問 URL http://localhost:8080/PageRedirect 來調用這個 Servlet。網(wǎng)頁將轉到給定的 URL http://www.runoob.com铡俐。
Servlet 文件上傳
Servlet 可以與 HTML form 標簽一起使用凰兑,來允許用戶上傳文件到服務器。上傳的文件可以是文本文件或圖像文件或任何文檔审丘。
文件上傳注意事項:
- 為了保證服務器安全吏够,上傳文件應該放在外界無法直接訪問的目錄下,比如防御WEB-INF下,
- 為防止文件覆蓋的現(xiàn)象發(fā)生锅知,要為上傳文件產(chǎn)生一個唯一的文件名
- 要限制上傳文件的最大值
- 可以限制上傳文件的類型播急,在收到上傳文件名時,判斷后綴名是否合法
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上傳實例 - 菜鳥教程</title>
</head>
<body>
<h1>文件上傳實例 - 菜鳥教程</h1>
<form method="post" action="/TomcatTest/UploadServlet" enctype="multipart/form-data">
選擇一個文件:
<input type="file" name="uploadFile" />
<br/><br/>
<input type="submit" value="上傳" />
</form>
</body>
</html>
表單如果包含一個上傳文件輸入項的話售睹,這個表單的enctype屬性就必須設置為multipart/form-data
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 上傳文件存儲目錄
private static final String UPLOAD_DIRECTORY = "upload";
// 上傳配置
private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB
private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB
private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB
/**
* 上傳數(shù)據(jù)及保存文件
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// 檢測是否為多媒體上傳
if (!ServletFileUpload.isMultipartContent(request)) {
// 如果不是則停止
PrintWriter writer = response.getWriter();
writer.println("Error: 表單必須包含 enctype=multipart/form-data");
writer.flush();
return;
}
// 配置上傳參數(shù)
DiskFileItemFactory factory = new DiskFileItemFactory();
// 設置內存臨界值 - 超過后將產(chǎn)生臨時文件并存儲于臨時目錄中
factory.setSizeThreshold(MEMORY_THRESHOLD);
// 設置臨時存儲目錄
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload upload = new ServletFileUpload(factory);
// 設置最大文件上傳值
upload.setFileSizeMax(MAX_FILE_SIZE);
// 設置最大請求值 (包含文件和表單數(shù)據(jù))
upload.setSizeMax(MAX_REQUEST_SIZE);
// 中文處理
upload.setHeaderEncoding("UTF-8");
// 構造臨時路徑來存儲上傳的文件
// 這個路徑相對當前應用的目錄
String uploadPath = request.getServletContext().getRealPath("./") + File.separator + UPLOAD_DIRECTORY;
// 如果目錄不存在則創(chuàng)建
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
try {
// 解析請求的內容提取文件數(shù)據(jù)
@SuppressWarnings("unchecked")
List<FileItem> formItems = upload.parseRequest(request);
if (formItems != null && formItems.size() > 0) {
// 迭代表單數(shù)據(jù)
for (FileItem item : formItems) {
// 處理不在表單中的字段
if (!item.isFormField()) {
String fileName = new File(item.getName()).getName();
String filePath = uploadPath + File.separator + fileName;
File storeFile = new File(filePath);
// 在控制臺輸出文件的上傳路徑
System.out.println(filePath);
// 保存文件到硬盤
item.write(storeFile);
request.setAttribute("message",
"文件上傳成功!");
}
}
}
} catch (Exception ex) {
request.setAttribute("message",
"錯誤信息: " + ex.getMessage());
}
// 跳轉到 message.jsp
request.getServletContext().getRequestDispatcher("/message.jsp").forward(
request, response);
}
}
Servlet 點擊計數(shù)器
網(wǎng)頁點擊計數(shù)器
以下是實現(xiàn)一個簡單的基于 Servlet 生命周期的網(wǎng)頁點擊計數(shù)器需要采取的步驟:
- 在 init() 方法中初始化一個全局變量旅择。
- 每次調用 doGet() 或 doPost() 方法時,都增加全局變量侣姆。
- 如果需要生真,您可以使用一個數(shù)據(jù)庫表來存儲全局變量的值在 destroy() 中。在下次初始化 Servlet 時捺宗,該值可在 init() 方法內被讀取柱蟀。這一步是可選的。
- 如果您只想對一個 session 會話計數(shù)一次頁面點擊蚜厉,那么請使用 isNew() 方法來檢查該 session 會話是否已點擊過相同頁面长已。這一步是可選的。
- 您可以通過顯示全局計數(shù)器的值昼牛,來在網(wǎng)站上展示頁面的總點擊量术瓮。這一步是可選的。
@WebServlet("/PageHitCounter")
public class PageHitCounter extends HttpServlet {
private static final long serialVersionUID = 1L;
private int hitCount;
public void init()
{
// 重置點擊計數(shù)器
hitCount = 0;
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
// 增加 hitCount
hitCount++;
PrintWriter out = response.getWriter();
out.println(hitCount);
}
public void destroy()
{
// 這一步是可選的贰健,但是如果需要胞四,您可以把 hitCount 的值寫入到數(shù)據(jù)庫
}
}
網(wǎng)站點擊計數(shù)器
很多時候,您可能有興趣知道整個網(wǎng)站的總點擊量伶椿。在 Servlet 中辜伟,這也是非常簡單的,我們可以使用過濾器做到這一點脊另。
以下是實現(xiàn)一個簡單的基于過濾器生命周期的網(wǎng)站點擊計數(shù)器需要采取的步驟:
- 在過濾器的 init() 方法中初始化一個全局變量导狡。
- 每次調用 doFilter 方法時,都增加全局變量偎痛。
- 如果需要旱捧,您可以在過濾器的 destroy() 中使用一個數(shù)據(jù)庫表來存儲全局變量的值。在下次初始化過濾器時踩麦,該值可在 init() 方法內被讀取, 這一步是可選的枚赡。
public class SiteHitCounter implements Filter{
private int hitCount;
public void init(FilterConfig config)
throws ServletException{
// 重置點擊計數(shù)器
hitCount = 0;
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws java.io.IOException, ServletException {
// 把計數(shù)器的值增加 1
hitCount++;
// 輸出計數(shù)器
System.out.println("網(wǎng)站訪問統(tǒng)計:"+ hitCount );
// 把請求傳回到過濾器鏈
chain.doFilter(request,response);
}
public void destroy()
{
// 這一步是可選的,但是如果需要靖榕,您可以把 hitCount 的值寫入到數(shù)據(jù)庫
}
}
....
<filter>
<filter-name>SiteHitCounter</filter-name>
<filter-class>SiteHitCounter</filter-class>
</filter>
<filter-mapping>
<filter-name>SiteHitCounter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
....
Servlet 自動刷新頁面
Java Servlet 提供了一個機制标锄,使得網(wǎng)頁會在給定的時間間隔自動刷新。
刷新網(wǎng)頁的最簡單的方式是使用響應對象的方法 setIntHeader()茁计。以下是這種方法的定義:
public void setIntHeader(String header, int headerValue)
此方法把頭信息 "Refresh" 連同一個表示時間間隔的整數(shù)值(以秒為單位)發(fā)送回瀏覽器料皇。
@WebServlet("/Refresh")
public class Refresh extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 設置刷新自動加載的事件間隔為 5 秒
response.setIntHeader("Refresh", 5);
// 設置響應內容類型
response.setContentType("text/html;charset=UTF-8");
// 獲取當前的時間
Calendar calendar = new GregorianCalendar();
String am_pm;
int hour = calendar.get(Calendar.HOUR);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
if(calendar.get(Calendar.AM_PM) == 0)
am_pm = "AM";
else
am_pm = "PM";
String CT = hour+":"+ minute +":"+ second +" "+ am_pm;
PrintWriter out = response.getWriter();
out.println(CT);
}
}