一、Servlet概述
- 什么是Servlet
Servlet是基于Java語(yǔ)言的Web服務(wù)器端編程技術(shù),按照J(rèn)ava EE規(guī)范定義,Servlet是運(yùn)行在Servlet容器中的Java類(lèi),它能處理Web客戶的HTTP請(qǐng)求,并產(chǎn)生HTTP響應(yīng)。
- Servlet對(duì)請(qǐng)求的處理和響應(yīng)過(guò)程分為如下幾個(gè)步驟:
接收HTTP請(qǐng)求;
取得請(qǐng)求信息,包括請(qǐng)求頭和請(qǐng)求參數(shù)數(shù)據(jù);
調(diào)用其他Java類(lèi)方法,完成具體的業(yè)務(wù)功能;
實(shí)現(xiàn)到其他Web組件的跳轉(zhuǎn)(包括重定向或請(qǐng)求轉(zhuǎn)發(fā));
生成HTTP響應(yīng)(包括HTML或非HTML響應(yīng))。
- 創(chuàng)建第一個(gè)Servlet
1.創(chuàng)建Java Web項(xiàng)目
2.創(chuàng)建Servlet
3.實(shí)現(xiàn)doPost()或doGet()方法
4.聲明配置Servlet:
3.x版本以上規(guī)范中使用注解聲明配置@WebServlet("/hello")2.5版本規(guī)范中采用web.xml配置文件聲明配置
5.部署運(yùn)行Servlet
- Servlet體系結(jié)構(gòu)
Servlet是使用Servlet API(應(yīng)用程序設(shè)計(jì)接口)及相關(guān)類(lèi)和方法的Java程序。
Servlet API包含兩個(gè)軟件包:
- javax.servlet包
定義了所有Servlet類(lèi)都必須實(shí)現(xiàn)或繼承的通用接口和類(lèi)javax.servlet.http包
定義了采用HTTP協(xié)議通信的HttpServlet類(lèi)。
Servlet API的主要接口和類(lèi)之間的關(guān)系
- Servlet聲明配置
Servlet聲明配置樣例
- loadOnStartup
指定Servlet的加載順序。當(dāng)此選項(xiàng)沒(méi)有指定時(shí),表示容器在該Servlet第一次被請(qǐng)求時(shí)才加載;當(dāng)值為0或者大于0時(shí),表示容器在應(yīng)用啟動(dòng)時(shí)就加載這個(gè)Servlet。值越小,啟動(dòng)該servlet的優(yōu)先級(jí)越高。原則上不同的Servlet應(yīng)該使用不同的啟動(dòng)順序數(shù)字
- init(ServletConfig config)
Servlet初始化方法,在Servlet實(shí)例化后,容器調(diào)用該方法進(jìn)行Servlet的初始化,init()方法只能被調(diào)用一次,如果此方法沒(méi)有正常結(jié)束,就會(huì)拋出一個(gè)ServletException異常,一旦拋出該異常,Servlet將不再執(zhí)行,隨后對(duì)其進(jìn)行再次調(diào)用會(huì),容器會(huì)重新載入并再次運(yùn)行init()方法。
- Servlet的映射路徑
urlPatterns= {"/hello","/hello.do","/hello/hello.html"} urlPatterns= {"/hello","*.do"}1.精確匹配:
/hello
訪問(wèn)路徑:http://localhost:8080/HelloServlet/hello
/hello/hello.do
訪問(wèn)路徑:http://localhost:8080/HelloServlet/hello/hello.do
2.模糊匹配:
/*
訪問(wèn)路徑:http://localhost:8080/HelloServlet/任意路徑
/hello/*
訪問(wèn)路徑: http://localhost:8080/HelloServlet/hello/任意路徑
.do、.action、*.后綴名
訪問(wèn)路徑: http://localhost:8080/HelloServlet/任意路徑.do
- Servlet2.5版本聲明配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >xmlns="http://xmlns.jcp.org/xml/ns/javaee" >xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee >http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" >version="3.1"> <display-name>ch02-HelloServlet2.5</display-name> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.neuedu.servlet.HelloServlet</servlet-class> <load-on-startup>0</load-on-startup> <init-param> <param-name>name</param-name> <param-value>neuedu</param-value> </init-param> <init-param> <param-name>email</param-name> <param-value>xxx@neuedu.com</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello.do</url-pattern> </servlet-mapping> </web-app>
- 在web.xml中通過(guò)“<servlet> </servlet>”元素聲明Servlet
<display-name>:指定該Servlet的顯示名,通常配合工具使用,等價(jià)于@WebServlet的displayName屬性
<servlet-name>:指定Servlet的名稱(chēng),一般與Servlet的類(lèi)名相同,要求在一個(gè)web.xml文件內(nèi)名字唯一,等價(jià)于@WebServlet的name屬性
<servlet-class>:指定Servlet類(lèi)的全限定名,即:包名.類(lèi)名
<init-param>:指定Servlet初始化參數(shù),等價(jià)于@WebServlet的initParams屬性,若有多個(gè)參數(shù)可重復(fù)定義此元素。此元素為可選配置
<param-name>:指定初始參數(shù)名
<param-value>:指定初始參數(shù)名對(duì)應(yīng)的值
<load-on-startup>:指定Servlet的加載順序,等價(jià)于@WebServlet的loadOnStartup屬性- web.xml中"<servlet-mapping> </servlet-mapping>”元素用于指定Servlet的URL映射
<servlet-name>:用來(lái)指定要映射的Servlet名稱(chēng),要與<servlet>聲明中的<servlet-name>值一致
<url-pattern>:指定Servlet的URL匹配模式,等價(jià)于@WebServlet的urlPatterns屬性或value屬性
- Servlet生命周期
Servlet生命周期是指Servlet實(shí)例從創(chuàng)建到響應(yīng)客戶請(qǐng)求,直至銷(xiāo)毀的過(guò)程。Servlet程序本身不直接在Java虛擬機(jī)上運(yùn)行,由Servlet容器負(fù)責(zé)管理其整個(gè)生命周期。
Servlet生命周期可分為四個(gè)階段:實(shí)例化、初始化、處理請(qǐng)求、銷(xiāo)毀。
- Servlet容器在如下時(shí)刻加載和實(shí)例化一個(gè)Servlet:
在Servlet容器啟動(dòng)后,客戶首次向Servlet發(fā)出請(qǐng)求,Servlet容器會(huì)判斷內(nèi)存中是否存在指定的Servlet對(duì)象,如果沒(méi)有則創(chuàng)建它,然后根據(jù)客戶的請(qǐng)求創(chuàng)建HttpRequest、HttpResponse對(duì)象,從而調(diào)用Servlet 對(duì)象的service方法。
在為Servlet配置了自動(dòng)裝入選項(xiàng)(load-on-startup)時(shí),服務(wù)器在啟動(dòng)時(shí)會(huì)自動(dòng)裝入此Servlet。- Servlet初始化
Servlet實(shí)例化后,Servlet容器將調(diào)用Servlet的init方法來(lái)對(duì)Servlet實(shí)例進(jìn)行初始化,初始化成功,Servlet在Web容器中會(huì)處于服務(wù)可用狀態(tài);如果初始化失敗,Servlet容器會(huì)銷(xiāo)毀該實(shí)例;當(dāng)Servlet運(yùn)行出現(xiàn)異常時(shí),Servlet容器會(huì)使該實(shí)例變?yōu)榉?wù)不可用狀態(tài)。- 請(qǐng)求處理
服務(wù)器接收到客戶端請(qǐng)求,會(huì)為該請(qǐng)求創(chuàng)建“請(qǐng)求”對(duì)象和“響應(yīng)”對(duì)象,并調(diào)用service()方法,service()方法再調(diào)用其他方法來(lái)處理請(qǐng)求。
在Servlet生命周期中,service()方法可能被多次調(diào)用。當(dāng)多個(gè)客戶端同時(shí)訪問(wèn)某個(gè)Servlet的service()方法時(shí),服務(wù)器會(huì)為每個(gè)請(qǐng)求創(chuàng)建一個(gè)線程,這樣可以并行處理多個(gè)請(qǐng)求,減少請(qǐng)求處理的等待時(shí)間,提高服務(wù)器的響應(yīng)速度。但同時(shí)也要注意對(duì)同一對(duì)象的并發(fā)訪問(wèn)問(wèn)題。- 服務(wù)終止
當(dāng)Servlet容器需要終止Servlet(如Web服務(wù)器被關(guān)閉或需要出讓資源),它會(huì)先調(diào)用Servlet的destroy()方法使其釋放正在使用的資源。
在調(diào)用destroy()方法之前,必須讓當(dāng)前正在執(zhí)行service()方法的任何線程完成執(zhí)行,或者超過(guò)了服務(wù)器定義的時(shí)間限制。
在destroy()方法完成后,Servlet容器必須釋放Servlet實(shí)例以便被垃圾回收。
二、Servlet基本應(yīng)用
- 處理超鏈接請(qǐng)求數(shù)據(jù)
- 超鏈接形式的數(shù)據(jù)請(qǐng)求語(yǔ)法格式:
<a href=”URL地址?參數(shù)=參數(shù)值[&參數(shù)=參數(shù)值...]”>鏈接文本</a>
發(fā)送請(qǐng)求的URL地址可以是絕對(duì)地址,
如:http://localhost:8080/ServletDemo1/QueryServlet。
也可以是相對(duì)地址,如:QueryServlet、../QueryServlet等形式。
在開(kāi)發(fā)中大多數(shù)使用相對(duì)地址,以便于項(xiàng)目的移植。<a href="UserQueryServlet?name=neuedu&email=neuedu@neuedu.com">查詢</a><br>protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { String name = request.getParameter("name"); String email = request.getParameter("email"); response.getWriter().append("name:"+ name + ",email:" + email); }查詢結(jié)果
- 處理Form表單請(qǐng)求數(shù)據(jù)
- Form表單數(shù)據(jù)請(qǐng)求語(yǔ)法格式:
<form action="UserQueryServlet" method = "post">
用戶名:<input type = "text" name = "name">
郵箱:<input type = "text" name = "email">
個(gè)人愛(ài)好:<input type="checkbox" name="love" value="1">吃飯
<input type="checkbox" name="love" value="2">睡覺(jué)
<input type="checkbox" name="love" value="3">打豆豆
<input type = "submit" value = "查詢">
</form>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
String name = request.getParameter("name");
String email = request.getParameter("email");
//獲取復(fù)選框的值
String [] love = request.getParameterValues("love");
response.getWriter().append("name: " + name + ",email:" + email+ ",love:" + love);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
- 超鏈接一般用于獲取/查詢資源信息,屬于GET請(qǐng)求類(lèi)型,請(qǐng)求的數(shù)據(jù)會(huì)附在URL之后,以?分割URL和傳輸數(shù)據(jù),參數(shù)之間以&相連。由于其安全性(如:請(qǐng)求數(shù)據(jù)會(huì)以明文顯示在地址欄上)以及請(qǐng)求地址的長(zhǎng)度限制,一般僅用于傳送一些簡(jiǎn)單的數(shù)據(jù);
- Form表單一般用于更新資源信息,默認(rèn)使用GET請(qǐng)求類(lèi)型,多使用POST請(qǐng)求類(lèi)型。由于POST請(qǐng)求類(lèi)型理論上沒(méi)有數(shù)據(jù)大小限制,可用表單來(lái)傳較大量的數(shù)據(jù);
- 重定向
重定向是指由原請(qǐng)求地址重新定位到某個(gè)新地址,原有的request請(qǐng)求失效,客戶端看到的是新的request請(qǐng)求返回的響應(yīng)結(jié)果,客戶端瀏覽器地址欄變?yōu)樾抡?qǐng)求地址。
- 重定向通過(guò)HttpServletResponse對(duì)象的sendRedirect()方法實(shí)現(xiàn),該方法會(huì)通知客戶端去重新訪問(wèn)新指定的URL地址,其語(yǔ)法格式如下:
public void sendRedirect(String location)throws java.io.IOException
location參數(shù)用以指定重定向的URL,它可以是相對(duì)路徑或絕對(duì)路徑。
sendRedirect()方法不僅可以重定向到當(dāng)前應(yīng)用程序中的其他資源,還可以重定向到同一個(gè)站點(diǎn)上的其他應(yīng)用程序中的資源,甚至是使用絕對(duì)URL重定向到其他站點(diǎn)的資源。@WebServlet("/RedirectServlet") public class RedirectServlet extends HttpServlet { protected void doGet(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { System.out.println("重定向前....."); response.sendRedirect("ResultServlet"); //response.sendRedirect("https://www.baidu.com"); System.out.println("重定向后....."); } protected void doPost(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }@WebServlet("/ResultServlet") public class ResultServlet extends HttpServlet { protected void doGet(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { // 設(shè)置響應(yīng)到客戶端的文本類(lèi)型為HTML response.setContentType("text/html;charset=UTF-8"); // 獲取輸出流 PrintWriter out = response.getWriter(); // 輸出響應(yīng)結(jié)果 out.println("重定向與請(qǐng)求轉(zhuǎn)發(fā)的結(jié)果頁(yè)面"); } protected void doPost(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- 請(qǐng)求轉(zhuǎn)發(fā)
請(qǐng)求轉(zhuǎn)發(fā)是指將請(qǐng)求再轉(zhuǎn)發(fā)到其他地址,轉(zhuǎn)發(fā)過(guò)程中使用的是同一個(gè)request請(qǐng)求,轉(zhuǎn)發(fā)后瀏覽器地址欄內(nèi)容不變。
- 請(qǐng)求轉(zhuǎn)發(fā)的過(guò)程發(fā)生在服務(wù)器內(nèi)部,只能從當(dāng)前應(yīng)用內(nèi)部查找相應(yīng)的轉(zhuǎn)發(fā)資源,而不能轉(zhuǎn)發(fā)到其它應(yīng)用的資源。
- 請(qǐng)求轉(zhuǎn)發(fā)使用RequestDispatcher接口中的forward()方法來(lái)實(shí)現(xiàn),該方法可以把請(qǐng)求轉(zhuǎn)發(fā)給另外一個(gè)資源,并讓該資源對(duì)此請(qǐng)求進(jìn)行響應(yīng)。
RequestDispatcher接口有以下兩個(gè)方法:
forward()方法:將請(qǐng)求轉(zhuǎn)發(fā)給其他資源。
include()方法:將其他資源并入到當(dāng)前請(qǐng)求中。
- 請(qǐng)求轉(zhuǎn)發(fā)語(yǔ)法:
RequestDispatcher dispatcher = request.getRequestDispatcher(String path);
dispatcher.forward(ServletRequest request,ServletResponse response);
其中:
path參數(shù)用以指定轉(zhuǎn)發(fā)的URL,只能是相對(duì)路徑;
request和response參數(shù)取值為當(dāng)前請(qǐng)求所對(duì)應(yīng)的HttpServletRequest和> > HttpServletResponse對(duì)象。@WebServlet("/ForwardServlet") public class ForwardServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { System.out.println("請(qǐng)求轉(zhuǎn)發(fā)前....."); request.getRequestDispatcher("ResultServlet").forward(request, >response); System.out.println("請(qǐng)求轉(zhuǎn)發(fā)后....."); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { doGet(request, response); } }
- 請(qǐng)求轉(zhuǎn)發(fā)與重定向數(shù)據(jù)傳遞
- 請(qǐng)求轉(zhuǎn)發(fā)與重定向?qū)ttpServletRequest對(duì)象屬性的存取。
HttpServletRequest對(duì)象屬性的存取語(yǔ)法:
request.setAttribute("attrobj", “value"); // 將attrobj屬性值存儲(chǔ)到request對(duì)象中
request.getAttribute("attrobj"); // 從request對(duì)象中取出attrobj屬性值
RedirectServlet中設(shè)置msg屬性值:
request.setAttribute("msg", "request redirect...");ForwardServlet中設(shè)置msg屬性值:
request.setAttribute("msg", "request forward...");ResultServlet中獲取msg屬性值:
String msg = (String)request.getAttribute("msg"); PrintWriter out = response.getWriter(); out.println(msg);
結(jié)果:
請(qǐng)求轉(zhuǎn)發(fā)
重定向
- 請(qǐng)求轉(zhuǎn)發(fā)與重定向的區(qū)別
重定向和請(qǐng)求轉(zhuǎn)發(fā)都可以讓瀏覽器獲得另外一個(gè)URL所指向的資源,但兩者的內(nèi)部運(yùn)行機(jī)制有很大的區(qū)別:
- 請(qǐng)求轉(zhuǎn)發(fā)只能將請(qǐng)求轉(zhuǎn)發(fā)給同一個(gè)Web應(yīng)用中的組件;
而重定向不僅可以重定向到當(dāng)前應(yīng)用程序中的其他資源,還可以重定向到同一個(gè)站點(diǎn)上的其他應(yīng)用程序中的資源,或者重定向到其他站點(diǎn)的資源;- 重定向的訪問(wèn)過(guò)程結(jié)束后,瀏覽器地址欄中顯示的URL會(huì)發(fā)生改變,由初始的URL地址變成重定向的目標(biāo)URL;
而請(qǐng)求轉(zhuǎn)發(fā)過(guò)程結(jié)束后,瀏覽器地址欄保持初始的URL地址不變;- 請(qǐng)求轉(zhuǎn)發(fā)調(diào)用者與被調(diào)用者之間共享相同的request對(duì)象和response對(duì)象,它們屬于同一個(gè)訪問(wèn)請(qǐng)求和響應(yīng)過(guò)程;
而重定向調(diào)用者與被調(diào)用者使用各自的request對(duì)象和response對(duì)象,它們屬于兩個(gè)獨(dú)立的訪問(wèn)請(qǐng)求和響應(yīng)過(guò)程。- 重定向和請(qǐng)求轉(zhuǎn)發(fā)不代表方法的結(jié)束,下面的代碼還會(huì)繼續(xù)執(zhí)行,注意在轉(zhuǎn)發(fā)或重定向的語(yǔ)句后面不要在編寫(xiě)代碼。
三、ServletConfig、ServletContext接口
- ServletConfig接口
- 容器在初始化一個(gè)Servlet時(shí),將為該Servlet創(chuàng)建一個(gè)唯一的ServletConfig對(duì)象,并將這個(gè)ServletConfig對(duì)象通過(guò)init(ServletConfig config)方法傳遞并保存在此Servlet對(duì)象中。
- 使用ServletConfig接口獲取Servlet初始化參數(shù)
通過(guò)使用@WebServlet注解的initParams熟悉來(lái)配置初始化參數(shù)(2.5版本中使用web.xml配置文件方式)@WebServlet(urlPatterns="/hello",loadOnStartup=1, initParams= {@WebInitParam(name="jdbcDriver",value="oracle.jdbc.driver.OracleDriver") , @WebInitParam(name="jdbcUrl",value="thin:2521")}) public class RequestWebInfoServlet extends HttpServlet { //init方法 @Override public void init(ServletConfig config) throws ServletException { String jdbcDriver = config.getInitParameter("jdbcDriver"); String jdbcUrl = config.getInitParameter("jdbcUrl"); System.out.println("request info Servlet init : jdbcDriver:" + >jdbcDriver +"," + "jdbcUrl" + jdbcUrl); } }
- ServletContext接口
- ServletContext也稱(chēng)為Servlet上下文,代表當(dāng)前Servlet運(yùn)行環(huán)境,是Servlet與Servlet容器之間直接通信的接口;
Servlet容器在啟動(dòng)一個(gè)Web應(yīng)用時(shí),會(huì)為該應(yīng)用創(chuàng)建一個(gè)唯一的ServletContext對(duì)象供該應(yīng)用中的所有Servlet對(duì)象共享,Servlet對(duì)象可以通過(guò)ServletContext對(duì)象來(lái)訪問(wèn)容器中的各種資源。- 獲得ServletContext對(duì)象的兩種方式:
通過(guò)ServletConfig接口的getServletContext()方法獲得ServletContext對(duì)象;
通過(guò)GenericServlet抽象類(lèi)的getServletContext()方法獲得ServletContext對(duì)象,實(shí)質(zhì)上該方法也是調(diào)用了ServletConfig的getServletContext()方法。- ServletContext接口中提供了以下幾種類(lèi)型的方法:
獲取應(yīng)用范圍的初始化參數(shù)的方法;
存取應(yīng)用范圍域?qū)傩缘姆椒ǎ?br> 獲取當(dāng)前Web應(yīng)用信息的方法;
獲取當(dāng)前容器信息和輸出日志的方法;
獲取服務(wù)器端文件資源的方法。- 配置ServletContext初始化參數(shù)
<!--配置應(yīng)用初始化參數(shù)(ServletContext) --> <context-param> <param-name>ctxParam</param-name> <param-value>ctxValue</param-value> </context-param>
- servlet文件中獲取ServletContext初始化參數(shù)
@Override public void init(ServletConfig config) throws ServletException { ServletContext ctx = config.getServletContext(); String param = ctx.getInitParameter("ctxParam"); System.out.println(param); }
- ServletContext域?qū)傩?/li>
- ServletContext對(duì)象可以理解為容器內(nèi)的一個(gè)共享空間,可以存放具有應(yīng)用級(jí)別作用域的數(shù)據(jù),Web應(yīng)用中的各個(gè)組件都可以共享這些數(shù)據(jù)。這些共享數(shù)據(jù)以key/value的形式存放在ServletContext對(duì)象中,并以key作為其屬性名被訪問(wèn)。
- 應(yīng)用域?qū)傩缘拇嫒》椒?br> setAttribute(String name,Object object),把一個(gè)對(duì)象和一個(gè)屬性名綁定并存放到ServletContext中,參數(shù)name指定屬性名,參數(shù)Object表示共享數(shù)據(jù)
getAttribute(String name),根據(jù)參數(shù)給定的屬性名,返回一個(gè)Object類(lèi)型的對(duì)象
getAttributeNames(),返回一個(gè)Enumeration對(duì)象,該對(duì)象包含了所有存放在ServletContext中的屬性名。
removeAttribute(String name),根據(jù)參數(shù)指定的屬性名,從ServletContext對(duì)象中刪除匹配的屬性- 頁(yè)面訪問(wèn)量統(tǒng)計(jì)
@WebServlet("/ContextAttributeServlet") public class ContextAttributeServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // 設(shè)置響應(yīng)到客戶端MIME類(lèi)型和字符編碼方式 response.setContentType("text/html;charset=UTF-8"); //獲取ServletContext對(duì)象 ServletContext ctx = getServletContext(); //從ctx對(duì)象獲取count屬性存放的計(jì)數(shù)值 Integer count = (Integer) ctx.getAttribute("count"); if (count == null) {//判斷是否為第一次訪問(wèn)頁(yè)面 count = 1; } else { count = count + 1; } //將更新后的數(shù)值存放到ServletContext對(duì)象的count屬性中 ctx.setAttribute("count", count); //獲取輸出流 PrintWriter out = response.getWriter(); //輸出計(jì)數(shù)信息 out.println("<p>本網(wǎng)站目前訪問(wèn)人數(shù)是: " + count + "</p>"); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
- 獲取Web應(yīng)用信息
ServletContext對(duì)象還包含有關(guān)Web應(yīng)用的信息,
例如:當(dāng)前Web應(yīng)用的根路徑、應(yīng)用的名稱(chēng)、應(yīng)用組件間的轉(zhuǎn)發(fā)、以及容器下其他Web應(yīng)用的ServletContext對(duì)象等。@WebServlet("/ContextAppInfoServlet") public class ContextAppInfoServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // 設(shè)置響應(yīng)到客戶端MIME類(lèi)型和字符編碼方式 response.setContentType("text/html;charset=UTF-8"); // 獲取當(dāng)前ServletContext對(duì)象 ServletContext context = super.getServletContext(); // 獲取容器中URL路徑為“/HelloServlet”的應(yīng)用的ServletContext對(duì)象 ServletContext contextByUrl = context.getContext("/HelloServlet"); // 獲取當(dāng)前Web應(yīng)用的上下文根路徑 String contextPath = context.getContextPath(); // 獲取當(dāng)前Web應(yīng)用的名稱(chēng) String contextName = context.getServletContextName(); // 獲取容器中URL路徑為“/chapter01”的應(yīng)用的應(yīng)用名稱(chēng) String contextByUrlName = contextByUrl.getServletContextName(); // 獲取轉(zhuǎn)發(fā)請(qǐng)求的RequestDispatcher對(duì)象 RequestDispatcher rd = >context.getRequestDispatcher("/HelloServlet"); // 獲取輸出流 PrintWriter out = response.getWriter(); out.println("<P>當(dāng)前Web應(yīng)用的上下文根路徑是:" + contextPath + >"</p>"); out.println("<p>當(dāng)前Web應(yīng)用的名稱(chēng)是:" + contextName + "</p>"); out.println("<p>容器中URL路徑為“/HelloServlet”的應(yīng)用的應(yīng)用名稱(chēng)是:" + contextByUrlName); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
四、Servlet請(qǐng)求與響應(yīng)
- HttpServletRequest接口
- ServletRequest接口被定義為用于封裝請(qǐng)求的信息,ServletRequest對(duì)象由Servlet容器在用戶每次請(qǐng)求Servlet時(shí)創(chuàng)建并傳入Servlet的service()方法中;
- HttpServletRequest接口繼承了ServletRequest接口,是專(zhuān)用于HTTP協(xié)議的子接口,用于封裝HTTP請(qǐng)求信息;
- 在HttpServlet類(lèi)的service()方法中,傳入的ServletRequest對(duì)象被強(qiáng)制轉(zhuǎn)換為HttpServletRequest對(duì)象來(lái)進(jìn)行HTTP請(qǐng)求信息的處理。
- HttpServletRequest接口提供了具有如下功能類(lèi)型的方法:
獲取請(qǐng)求報(bào)文信息(包括請(qǐng)求行、請(qǐng)求頭、請(qǐng)求正文)的方法;
獲取網(wǎng)絡(luò)連接信息的方法;
存取請(qǐng)求域?qū)傩缘姆椒ā?/li>
- HTTP協(xié)議
客戶端瀏覽器和服務(wù)器端Servlet通過(guò)HTTP協(xié)議進(jìn)行通信,HTTP協(xié)議采用請(qǐng)求/響應(yīng)模型,協(xié)議的請(qǐng)求報(bào)文由請(qǐng)求行、請(qǐng)求頭和可選的請(qǐng)求正文組成。
HTTP協(xié)議的請(qǐng)求報(bào)文請(qǐng)求報(bào)文信息格式樣例—我們可以通過(guò)firefox瀏覽器的開(kāi)發(fā)者工具查看
- HTTP協(xié)議請(qǐng)求正文內(nèi)容為POST請(qǐng)求參數(shù)名稱(chēng)和值所組成的一個(gè)字符,GET請(qǐng)求參數(shù)附屬在請(qǐng)求行中,沒(méi)有請(qǐng)求正文。
- HTTP請(qǐng)求參數(shù)中文亂碼問(wèn)題
在進(jìn)行請(qǐng)求參數(shù)傳遞時(shí),經(jīng)常會(huì)遇到請(qǐng)求數(shù)據(jù)為中文時(shí)的亂碼問(wèn)題,出現(xiàn)亂碼的原因與客戶端的請(qǐng)求編碼方式(GET請(qǐng)求或POST請(qǐng)求)以及服務(wù)器的處理編碼方式有關(guān)。
POST請(qǐng)求亂碼問(wèn)題:
- 瀏覽器會(huì)按當(dāng)前顯示頁(yè)面所采用的字符集對(duì)請(qǐng)求的中文數(shù)據(jù)進(jìn)行編碼,再以報(bào)文體的形式傳送給服務(wù)器,Servlet在調(diào)用getParameter()方法獲取參數(shù)時(shí),會(huì)以HttpServletRequest對(duì)象的getCharacterEncoding()方法返回的字符集對(duì)其進(jìn)行解碼,而該方法的返回值在未經(jīng)過(guò)setCharacterEncoding(charset)方法設(shè)置編碼的情況下為null,這時(shí)getParameter()方法將以服務(wù)器默認(rèn)的“ISO-8859-1”字符集對(duì)參數(shù)進(jìn)行解碼,而“ISO-8859-1”字符集并不包含中文,于是造成中文參數(shù)的亂碼問(wèn)題。
- POST中文亂碼解決辦法:
在調(diào)用getParameter()方法前先調(diào)用setCharacterEncoding(charset)方法設(shè)定與頁(yè)面請(qǐng)求編碼相同的解碼字符集。request.setCharacterEncoding("UTF-8");GET請(qǐng)求的亂碼問(wèn)題:
- GET請(qǐng)求參數(shù)以“?”或“&”為連接字符附加在URL地址后,根據(jù)網(wǎng)絡(luò)標(biāo)準(zhǔn)RFC1738規(guī)定,只有字母和數(shù)字以及一些特殊符號(hào)和某些保留字才可以不經(jīng)過(guò)編碼直接用于URL,因此在請(qǐng)求參數(shù)為中文時(shí)必須先由瀏覽器進(jìn)行編碼后才能發(fā)送給服務(wù)器,服務(wù)器端對(duì)GET請(qǐng)求參數(shù)依照服務(wù)器本身默認(rèn)的字符集進(jìn)行解碼。
- 在服務(wù)器端,由于GET請(qǐng)求參數(shù)是作為請(qǐng)求行發(fā)送給服務(wù)器的,因此Servlet在通過(guò)getParameter()獲取請(qǐng)求參數(shù)時(shí),并不能使用setCharacterEncoding(charset)方法指定的字符集進(jìn)行解碼,而是依照服務(wù)器本身默認(rèn)的字符集進(jìn)行解碼。
- Tomcat服務(wù)器各版本中默認(rèn)的URIEncoding字符集并不完全相同,例如,Tomcat6和Tomcat7都默認(rèn)為“ISO-8859-1”,這類(lèi)版本中,對(duì)于GET請(qǐng)求的中文參數(shù)必須經(jīng)處理后才會(huì)避免亂碼問(wèn)題,因此在實(shí)際開(kāi)發(fā)中盡量避免使用GET請(qǐng)求來(lái)傳遞中文參數(shù)。
- HttpServletRequest屬性
- 存儲(chǔ)在HttpServletRequest對(duì)象中的對(duì)象稱(chēng)之為請(qǐng)求域?qū)傩裕瑢儆谕粋€(gè)請(qǐng)求的多個(gè)處理組件之間可以通過(guò)請(qǐng)求域?qū)傩詠?lái)傳遞對(duì)象數(shù)據(jù)。
- HttpServletRequest接口提供了如下與請(qǐng)求域?qū)傩韵嚓P(guān)的方法:
void setAtrribute(String name,Object value),設(shè)定name屬性的值為value,保存在request范圍內(nèi);
Object getAttribute(String name),從request范圍內(nèi)獲取name屬性的值;
void removeAttribute(String name),從request范圍內(nèi)移除name屬性的值;
Enumeration getAttributeNames(),獲取所有request范圍的屬性
- HttpServletResponse接口
- ServletResponse接口被定義為用于創(chuàng)建響應(yīng)消息,ServletResponse對(duì)象由Servlet容器在用戶每次請(qǐng)求Servlet時(shí)創(chuàng)建并傳入Servlet的service()方法中。
- HttpServletResponse接口繼承自ServletResponse接口,是專(zhuān)用于HTTP協(xié)議的子接口,用于封裝HTTP響應(yīng)消息。
- 在HttpServlet類(lèi)的service()方法中,傳入的ServletResponse對(duì)象被強(qiáng)制轉(zhuǎn)換為HttpServletResponse對(duì)象來(lái)進(jìn)行HTTP響應(yīng)信息的處理。
- HttpServletResponse接口提供了具有如下功能類(lèi)型的方法:
設(shè)置響應(yīng)狀態(tài)的方法;
構(gòu)建響應(yīng)頭的方法;
創(chuàng)建響應(yīng)正文的方法。
將服務(wù)器的端口號(hào)調(diào)整為8888,以后的訪問(wèn)路徑為:http://localhost:8888/
- HTTP協(xié)議響應(yīng)報(bào)文
HTTP協(xié)議的響應(yīng)報(bào)文由響應(yīng)行、響應(yīng)頭和響應(yīng)正文組成。
HTTP協(xié)議響應(yīng)報(bào)文格式如下圖:
- HTTP協(xié)議響應(yīng)行
- HTTP協(xié)議響應(yīng)報(bào)文的響應(yīng)行由報(bào)文協(xié)議和版本以及狀態(tài)碼和狀態(tài)描述構(gòu)成,狀態(tài)碼由三個(gè)十進(jìn)制數(shù)字組成,第一個(gè)十進(jìn)制數(shù)字定義了狀態(tài)碼的類(lèi)型,后兩個(gè)數(shù)字沒(méi)有分類(lèi)的作用。HTTP狀態(tài)碼可分為如下5種類(lèi)型:
- 常見(jiàn)的響應(yīng)狀態(tài)碼:
200表示請(qǐng)求成功;
302表示資源(網(wǎng)頁(yè)等)暫時(shí)轉(zhuǎn)移到其它URL;
404表示請(qǐng)求的資源(網(wǎng)頁(yè)等)不存在;
500表示服務(wù)器內(nèi)部錯(cuò)誤。
- HTTP協(xié)議響應(yīng)消息頭
- 設(shè)置響應(yīng)消息頭
對(duì)于一些常用的消息頭, HttpServletResponse提供了一些特定的方法來(lái)進(jìn)行設(shè)置:
setContentType(String mime),設(shè)定Content-Type消息頭;
setContentLength(int length),設(shè)定Content-Length消息頭;
addHeader(String name,String value),新增String類(lèi)型的值到名為name的http頭部;
addIntHeader(String name,int value),新增int類(lèi)型的值到名為name的http頭部;
addDateHeader(String name,long date),新增long類(lèi)型的值到名為name的http頭部;
addCookie(Cookie c),為Set-Cookie消息頭增加一個(gè)值;
- 實(shí)現(xiàn)一個(gè)頁(yè)面動(dòng)態(tài)刷新效果
@WebServlet("/ResponseRefreshHeadServlet") public class ResponseRefreshHeadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 設(shè)置Content-Type消息頭響應(yīng)文檔MIME類(lèi)型和字符編碼方式 response.setContentType("text/html;charset=UTF-8"); // 設(shè)置響應(yīng)頭“refresh”的值為1秒 response.setHeader("refresh", "1"); //獲取輸出流 PrintWriter out = response.getWriter(); out.println("現(xiàn)在時(shí)間是:"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd >hh:mm:ss"); out.println(sdf.format(new Date())); } }
- 創(chuàng)建響應(yīng)正文
- 在Servlet中,向客戶端輸出的響應(yīng)數(shù)據(jù)是通過(guò)輸出流對(duì)象來(lái)完成的,HttpServletResponse接口提供了兩個(gè)獲取不同類(lèi)型輸出流對(duì)象的方法:
1.getOutputStream(),返回字節(jié)輸出流對(duì)象ServletOutputStream;
ServletOutputStream對(duì)象主要用于輸出二進(jìn)制字節(jié)數(shù)據(jù)。例如,配合setContentType()方法響應(yīng)輸出一個(gè)圖像、視頻等。
2.getWriter(),返回字符輸出流對(duì)象PrintWriter;
PrintWriter對(duì)象主要用于輸出字符文本內(nèi)容,但其內(nèi)部實(shí)現(xiàn)仍是將字符串轉(zhuǎn)換成某種字符集編碼的字節(jié)數(shù)組后再進(jìn)行輸出。- 當(dāng)向ServletOutputStream或PrintWriter對(duì)象中寫(xiě)入數(shù)據(jù)后,Servlet容器會(huì)將這些數(shù)據(jù)作為響應(yīng)消息的正文,然后再與響應(yīng)狀態(tài)行和各響應(yīng)頭組合成完整的響應(yīng)報(bào)文輸出到客戶端,同時(shí),在Serlvet的service()方法結(jié)束后,容器還將檢查getWriter()或getOutputStream()方法返回的輸出流對(duì)象是否已經(jīng)調(diào)用過(guò)close()方法,如果沒(méi)有,容器將調(diào)用close()方法關(guān)閉該輸出流對(duì)象。
- 實(shí)現(xiàn)響應(yīng)輸出一個(gè)圖像
@WebServlet("/OutputStreamServlet") public class OutputStreamServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //設(shè)置響應(yīng)消息頭Content-Type response.setContentType("image/jpeg"); // 獲取ServletContext對(duì)象 ServletContext context = super.getServletContext(); // 獲取讀取服務(wù)器端文件的輸入流 InputStream is = context.getResourceAsStream("/imgs/logo.png"); // 獲取ServletOutputStream輸出流 ServletOutputStream os = response.getOutputStream(); int i = 0; while ((i = is.read()) != -1) { os.write(i);//向輸出流中寫(xiě)入二進(jìn)制數(shù)據(jù) } is.close(); os.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
- 響應(yīng)輸出中文亂碼問(wèn)題
- 由于Java程序中的字符文本在內(nèi)存中以Unicode編碼的形式存在,因此PrintWriter對(duì)象在輸出字符文本時(shí),需要先將它們轉(zhuǎn)換成其他某種字符集編碼的字節(jié)數(shù)組后輸出。
- PrintWriter對(duì)象默認(rèn)使用ISO-8859-1字符集進(jìn)行Unicode字符串到字節(jié)數(shù)組的轉(zhuǎn)換,由于ISO-8859-1字符集中沒(méi)有中文字符,因此Unicode編碼的中文字符將被轉(zhuǎn)換成無(wú)效的字符編碼后輸出給客戶端,這就是Servlet中文輸出亂碼問(wèn)題的原因。
- ServletResponse接口中定義了三種方等方法來(lái)指定getWriter()方法返回的PrintWriter對(duì)象所使用的字符集編碼。
1.response.setCharacterEncoding("UTF-8");
只能用來(lái)設(shè)置PrintWriter輸出流中字符的編碼方式,它的優(yōu)先權(quán)最高。
2.(推薦使用)response.setContentType("text/html;charset=UTF-8");
既可以設(shè)置PrintWriter輸出流中字符的編碼方式,也可以設(shè)置瀏覽器接收到這些字符后以什么編碼方式來(lái)解碼,它的優(yōu)先權(quán)低于第一種方法。
3.response.setLocale(new java.util.Locale("zh","CN"));
只能用來(lái)設(shè)置PrintWriter輸出流中字符的編碼方式,它的優(yōu)先權(quán)最低,在已經(jīng)使用前兩種方法中的一個(gè)設(shè)置了編碼方式以后,它將被覆蓋而不再起作用。












