一、Servlet實(shí)現(xiàn)
1妓盲、Servlet簡(jiǎn)介
Servlet 是 Server 與 Applet 的縮寫杂拨,是服務(wù)端小程序的意思。使用 Java 語(yǔ) 言編寫的服務(wù) 器端程序悯衬,可以像生成動(dòng)態(tài)的 WEB 頁(yè)弹沽,Servlet 主要運(yùn)行在服務(wù)器端,并由服務(wù)器調(diào)用執(zhí)行筋粗, 是一種按照 Servlet 標(biāo)準(zhǔn)來(lái)開發(fā)的類策橘。 是 SUN 公 司提供的一門用于開發(fā)動(dòng)態(tài) Web 資源的技 術(shù)娜亿。(言外之意:要實(shí)現(xiàn) web 開發(fā)丽已, 需要實(shí)現(xiàn) Servlet 標(biāo)準(zhǔn)) 。
Servlet 本質(zhì)上也是 Java 類暇唾,但要遵循 Servlet 規(guī)范進(jìn)行編寫促脉,沒有 main()方法,它的創(chuàng) 建策州、使用瘸味、銷毀都由 Servlet 容器進(jìn)行管理(如 Tomcat)。(言外之 意:寫自己的類够挂,不用寫 main 方法旁仿,別人自動(dòng)調(diào)用)
Servlet 是和 HTTP 協(xié)議是緊密聯(lián)系的,其可以處理 HTTP 協(xié)議相關(guān)的所有 內(nèi)容孽糖。這也是 Servlet 應(yīng)用廣泛的原因之一枯冈。
提供了 Servlet 功能的服務(wù)器,叫做 Servlet 容器办悟,其常見容器有很多尘奏,如 Tomcat, Jetty, WebLogic Server, WebSphere, JBoss 等等。
2病蛉、實(shí)現(xiàn)Servlet步驟
* 實(shí)現(xiàn)步驟
新建普通java類
實(shí)現(xiàn)Servlet規(guī)范-----繼承HTTPServlet類
重寫service方法:
在servlet中默認(rèn)情況下炫加,無(wú)論你是get還是post 提交過(guò)來(lái) 都會(huì)經(jīng)過(guò)service()
方法來(lái)處理瑰煎,然后轉(zhuǎn)向到doGet 或doPost
配置web.xml,配置對(duì)外訪問路徑
將項(xiàng)目發(fā)布到服務(wù)器并運(yùn)行服務(wù)器
通過(guò)瀏覽器訪問項(xiàng)目中的資源
* 實(shí)現(xiàn)Servlet規(guī)范的三種方式
方式一:繼承HttpServlet類
方式二:繼承GenericServlet類
方式三:實(shí)現(xiàn)Servlet接口
* 訪問路徑
注意 url 的 格式正確俗孝,tomcat 的端口為 8080酒甸。
http://localhost:8080/hw/helloweb
協(xié)議:http
域名和端口號(hào)指定服務(wù)器:localhost:8080(如果沒有更改)
站點(diǎn)名:hw(項(xiàng)目的context root,通過(guò)站點(diǎn)找到服務(wù)器下的具體項(xiàng)目)
資源名:helloweb(web.xml中配置)
3赋铝、配置web.xml
基本配置
<!-- Servlet的配置 -->
<servlet>
<servlet-name>servlet01</servlet-name> <!-- 給服務(wù)器看的名字,servlet標(biāo)簽中servlet-name要與servlet-mapping標(biāo)簽中的servlet-name的值保持一致 -->
<servlet-class>com.shsxt.servlet.Servlet01</servlet-class> <!-- servlet類的路徑 -->
</servlet>
<servlet-mapping>
<servlet-name>servlet01</servlet-name> <!-- 給服務(wù)器看的名字插勤,servlet-mapping標(biāo)簽中的servlet-name的值要與servlet標(biāo)簽中servlet-name保持一致 -->
<url-pattern>/ser01</url-pattern> <!-- 對(duì)外訪問路徑 -->
</servlet-mapping>
* 詳細(xì)配置
* 1、初始化參數(shù)
* 設(shè)置在web.xml中的servlet標(biāo)簽中
* <init-param>
* <param-name><param-/name>
* <param-value></param-value>
* </init-param>
* 2革骨、自啟動(dòng)(服務(wù)器啟動(dòng)時(shí)自動(dòng)實(shí)例化servlet)
* 設(shè)置在web.xml中的servlet標(biāo)簽中农尖,要寫在init-param標(biāo)簽之后
* <load-on-startup>1</load-on-startup>
Servlet 容器啟動(dòng)時(shí)加載 Servlet 對(duì)象的順序
* 值越小,優(yōu)先級(jí)越高
* 3良哲、servlet配置多個(gè)訪問路徑
* 以Servlet05為例:
* a. 只設(shè)置一個(gè)路徑
* <url-pattern>/ser05</url-pattern>
* b.設(shè)置多個(gè)路徑
* <url-pattern>/ser05</url-pattern>
* <url-pattern>/ser005</url-pattern>
* c.以指定路徑開頭的所有資源路徑
* <url-pattern>/test/*</url-pattern>
* d.以指定后綴結(jié)尾的所有資源路徑
* <url-pattern>*.html</url-pattern>
* e.所有路徑都可以訪問
* <url-pattern>*</url-pattern>
*
* 通配符"*"
* 1卤橄、"*"只能放在最前面或者最后面,不能放在中間臂外,不能單獨(dú)使用窟扑,不能和字母拼接
* 2、值越精準(zhǔn)漏健,優(yōu)先級(jí)越高
*
* 4.注意事項(xiàng):
1嚎货、web.xml改動(dòng)之后必須重啟服務(wù)器
2、url-pattern的值必須唯一
異常:
java.lang.IllegalArgumentException:
The servlets named [servelt01] and
[servlet02] are both mapped to the
url-pattern [/ser01] which is not permitted
3蔫浆、servlet-mapping標(biāo)簽中的servlet-name的值必須是已存在的值
(servalet標(biāo)簽中的servlet-name)
異常:
java.lang.IllegalArgumentException:
Servlet mapping specifies an unknown
servlet name servelt02
4殖属、servlet標(biāo)簽中servlet-name要與servlet-mapping標(biāo)簽中的
servlet-name的值保持一致 (servlet標(biāo)簽中servlet-name存在)
如果不一致,代碼不會(huì)報(bào)錯(cuò)瓦盛,只是訪問的不是你的指定資源
5洗显、servlet標(biāo)簽中的servlet-class的路徑不能寫錯(cuò)
異常:
java.lang.ClassNotFoundException:
com.shsxt.servlet.Servlet04
6、servlet-mapping標(biāo)簽中的url-pattern的值前面必須加"/"
異常:
java.lang.IllegalArgumentException:
Invalid <url-pattern> ser01 in servlet mapping
7原环、servlet標(biāo)簽中的servlet-name的值必須唯一
異常:
Duplicate unique value [servlet01] declared for
identity constraint "web-app-servlet-name-uniqueness"
of element "web-app".
url-pattern配置示例:客戶訪問的 Servlet 的相對(duì) URL 路徑
</servlet>
<servlet-mapping>
<servlet-name>servlet05</servlet-name>
<!-- a. 只設(shè)置一個(gè)路徑 -->
<!-- <url-pattern>/ser05</url-pattern> -->
<!-- b.設(shè)置多個(gè)路徑 -->
<!-- <url-pattern>/ser05</url-pattern>
<url-pattern>/ser005</url-pattern> -->
<!--c.以指定路徑開頭的所有資源路徑 -->
<!-- <url-pattern>/test/*</url-pattern> -->
<!-- d.以指定后綴結(jié)尾的所有資源路徑 -->
<!-- <url-pattern>*.html</url-pattern> -->
<!-- 所有路徑都可以訪問 -->
<url-pattern>/*</url-pattern>
</servlet-mapping>
說(shuō)明: url-pattern 可以配多個(gè)(一個(gè) servlet 可以通過(guò)多個(gè) url-pattern 訪 問)
但多個(gè) servlet不能 配置成同一個(gè) url-pattern
4挠唆、Servlet生命周期
Servlet 沒有 main()方法,不能獨(dú)立運(yùn)行嘱吗,它的運(yùn)行完全由 Servlet 引擎來(lái)控 制和調(diào)度玄组。 所謂生命周期,指的是 servlet 容器何時(shí)創(chuàng)建 servlet 實(shí)例谒麦、何時(shí)調(diào) 用其方法進(jìn)行請(qǐng)求的處理俄讹、 何時(shí)并銷毀其實(shí)例的整個(gè)過(guò)程。(此處討論默認(rèn)的生 命周期)
* 實(shí)例和初始化時(shí)機(jī)
初始化绕德,服務(wù)器方式患膛,由服務(wù)器調(diào)用
只會(huì)調(diào)用一次,當(dāng)?shù)谝淮握?qǐng)求時(shí)
當(dāng)請(qǐng)求到達(dá)容器時(shí),容器查找該 servlet 對(duì)象是否存在,如果不存在耻蛇,則會(huì)創(chuàng)建實(shí)例并進(jìn)行初始化
* 就緒/調(diào)用/服務(wù)階段
服務(wù)/調(diào)用方法踪蹬,服務(wù)器方式驹溃,由服務(wù)器調(diào)用
可以調(diào)用多次,每次請(qǐng)求到達(dá)時(shí)都會(huì)調(diào)用
* 銷毀時(shí)機(jī)
服務(wù)器方式延曙,由服務(wù)器調(diào)用
只會(huì)調(diào)用一次,容器關(guān)閉時(shí)(正常關(guān)閉時(shí)才會(huì)打油龊濉)
5枝缔、 Tomcat 與 Servlet 交互時(shí)序
1、Web Client 向 Servlet 容器(Tomcat)發(fā)出 Http 請(qǐng)求
2蚊惯、Servlet 容器接收 Web Client 的請(qǐng)求
3愿卸、Servlet 容器創(chuàng)建一個(gè) HttpRequest 對(duì)象,
將 Web Client 請(qǐng)求的信息封 裝到這個(gè)對(duì)象 中
4截型、Servlet 容器創(chuàng)建一個(gè) HttpResponse 對(duì)象
5趴荸、Servlet 容器調(diào)用 HttpServlet 對(duì)象的 service 方法,把 HttpRequest對(duì)
象與 HttpResponse 對(duì)象作為參數(shù)宦焦,傳給 HttpServlet 對(duì)象
6发钝、HttpServlet 調(diào)用 HttpRequest 對(duì)象的有關(guān)方法,獲取 Http 請(qǐng)求信息
7波闹、HttpServlet 調(diào)用 HttpResponse 對(duì)象的有關(guān)方法酝豪,生成響應(yīng)數(shù)據(jù)
8、Servlet 容器把 HttpServlet 的響應(yīng)結(jié)果傳給 Web Client
6精堕、Servlet的工作原理及流程(重點(diǎn))
1)客戶端發(fā)出請(qǐng)求
2)根據(jù) web.xml 文件的配置孵淘,找到對(duì)應(yīng)的站點(diǎn)和資源
3)讀取XML文件中的值找到對(duì)應(yīng)的 找到該 class 并加載執(zhí)行該 class
4)返回結(jié)果 由 Web 服務(wù)器將結(jié)果響 應(yīng)給客戶端
二、請(qǐng)求的幾種方式
客戶端對(duì)服務(wù)器發(fā)起請(qǐng)求的幾種方式:
地址欄直接輸入歹篓、超鏈接瘫证、Form表單、ajax庄撮、請(qǐng)求轉(zhuǎn)發(fā)背捌、重定向
三、常用對(duì)象HttpServletRequest對(duì)象
HttpServletRequest 對(duì)象:主要作用是用來(lái)接收客戶端發(fā)送過(guò)來(lái)的請(qǐng)求信息
1洞斯、常用方法
// 獲取請(qǐng)求的完整路徑 (從http到"?"前面)
getRequestURL()
// 獲取請(qǐng)求的部分路徑 (從端口后"?"前面)
getRequestURI()
// 獲取請(qǐng)求的參數(shù)字符串 (從"?"開始到最后)
getQueryString()
// 獲取請(qǐng)求類型 (GET/POST)
getMethod()
// 獲取指定請(qǐng)求頭
getHeader( String)
// 獲取指定名稱的參數(shù)值(重點(diǎn))
getParameter(String);
2载萌、請(qǐng)求亂碼問題(GET/POST、Tomcat版本)
解決POST請(qǐng)求亂碼:
request.setCharacterEncoding("UTF-8"); // 設(shè)置服務(wù)器的編碼方式巡扇,該方式只對(duì)post請(qǐng)求生效扭仁,如果是get請(qǐng)求沒有任何效果
new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
解決GET請(qǐng)求亂碼:(如果原來(lái)不亂碼,通過(guò)new String()去處理厅翔,會(huì)出現(xiàn)另外一種亂碼)
new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
總結(jié) :
Post請(qǐng)求:無(wú)論什么版本的服務(wù)器乖坠,都亂碼
Get請(qǐng)求:Tomcat8及以上版本,不亂碼刀闷;Tomcat7及以下版本亂碼熊泵。
3仰迁、請(qǐng)求轉(zhuǎn)發(fā)(重點(diǎn))
request.getRequestDispatcher(url).forward(request, response);
特點(diǎn):
服務(wù)器行為
地址欄不會(huì)發(fā)生改變
只有一次請(qǐng)求,request對(duì)象共享
注意:使用之前不能關(guān)閉資源
4顽分、request域?qū)ο螅ㄖ攸c(diǎn))
將數(shù)據(jù)存到request作用域中徐许,前臺(tái)頁(yè)面(JSP)從作用域中獲取域?qū)ο蟮闹?/p>
方法:
* setAttribute(name,value):設(shè)置作用域,name是字符串類型卒蘸,value是object類型
* getAttribute(name):獲取指定名稱的域?qū)ο蟮闹荡朴纾祷氐氖莖bject類型
* removeAttribute(name):移除指定名稱的域?qū)ο蟮闹? 作用范圍:
在一次請(qǐng)求中有效,即 服務(wù)器跳轉(zhuǎn)有效缸沃。
request 域?qū)ο笾械臄?shù)據(jù)在一次請(qǐng)求中有效恰起,則經(jīng)過(guò)請(qǐng)求轉(zhuǎn)發(fā),request 域
中的數(shù)據(jù)依然存在趾牧,則在請(qǐng)求轉(zhuǎn)發(fā)的過(guò)程中可以通過(guò) request 來(lái)傳輸/共享數(shù)據(jù)检盼。
四、常用對(duì)象HttpServletResponse對(duì)象
1翘单、常用方法
// 設(shè)置響應(yīng)頭
// response.addHeader("uname", "zhangsan");
// 設(shè)置錯(cuò)誤狀態(tài)
// response.sendError(404,"由于顏值過(guò)低吨枉,無(wú)法訪問!");
// 設(shè)置refresh響應(yīng)頭
// response.setHeader("refresh", "2"); // 2秒刷新一次
response.setHeader("refresh", "2;URL=http://www.baidu.com");
// 2秒鐘后跳轉(zhuǎn)到百度
2哄芜、響應(yīng)數(shù)據(jù)
getWriter()獲取字符流(只能響應(yīng)回字符)东羹;
getOutputStream()獲取字節(jié)流(能響應(yīng)一切數(shù)據(jù))。
response.setContentType("application/json");// 設(shè)置響應(yīng)類型 默認(rèn)是純文本
響應(yīng)回的數(shù)據(jù)到客戶端被瀏覽器解析忠烛。
注意:兩者不能同時(shí)使用属提。
3、響應(yīng)亂碼問題
* 亂碼原因:
在響應(yīng)時(shí)美尸,服務(wù)器經(jīng)過(guò)網(wǎng)絡(luò)傳輸響應(yīng)數(shù)據(jù)到客戶端冤议,服務(wù)器默認(rèn)編碼為ISO-8859-1,該編碼不支持中文师坎;客戶端也有其默認(rèn)編碼恕酸。
總結(jié):1)服務(wù)端和客戶端編碼不一致;2)編碼不支持中文胯陋。
* 如何解決
1)統(tǒng)一客戶端和服務(wù)端編碼格式蕊温;2)使用支持中文的編碼格式
getWriter()的字符亂碼
響應(yīng)中文必定出亂碼,由于服務(wù)器端在進(jìn)行編碼時(shí)默認(rèn)會(huì)使用 ISO-8859-1 格式的編碼遏乔,
該編碼方式并不支持中文义矛。
1)// 設(shè)置服務(wù)端的編碼
//response.setCharacterEncoding("UTF-8");
// 設(shè)置客戶端的編碼
//response.setHeader("content-type", "text/html;charset=UTF-8");
2)// 同時(shí)設(shè)置客戶端和服務(wù)端的編碼
response.setContentType("text/html;charset=UTF-8");
//分別指定服務(wù)器和客戶端編碼
response.setCharacterEncoding("utf-8");
response.setHeader("content-type", "text/html;utf-8");
//或同時(shí)指定服務(wù)器和客戶
//response.setContentType("text/html;charset=utf-8");
String str="<h3>阿拉燈神丁<h3/>";
OutputStream os=response.getOutputStream();
os.write(str.getBytes());
os.close();
getOutputStream()字節(jié)亂碼
對(duì)于 getOutputStream()方式獲取到的字節(jié)流,響應(yīng)中文時(shí)盟萨,由于本身就是傳輸?shù)淖止?jié)凉翻,
所以此時(shí)可能出現(xiàn)亂碼,也可能正確顯示捻激。
無(wú)論如何我們都應(yīng)該準(zhǔn)確掌握服務(wù)器和客戶端使用的是那種編碼格式制轰, 以確保數(shù)據(jù)正確顯
示前计。指定客戶端和服務(wù)器使用的編碼方式一致即可 。
4垃杖、響應(yīng)圖片
有的時(shí)候我們并不知道(或不能確定)圖片的路徑男杈,需要通過(guò)請(qǐng)求服務(wù)器資源動(dòng)態(tài)地響應(yīng)圖片給客戶端。這種方式和文件拷貝的理念是一致的调俘,客戶端請(qǐng)求 服務(wù)器的資源伶棒,在服務(wù)端獲取到真實(shí)的圖片資源,通過(guò)輸入流讀取到內(nèi)存脉漏,然后通過(guò)輸出流寫出到客戶端即可。
步驟:
得到圖片存放的路徑
通過(guò)路徑得到文件對(duì)象
得到字節(jié)輸出流
得到文件對(duì)象的輸入流
循環(huán)讀取袖牙,并寫出到瀏覽器
注意:在客戶端解析資源時(shí)默認(rèn)是以文本(text/html)的形式侧巨,當(dāng) 響應(yīng)圖片時(shí)需要指定
響應(yīng)頭信息,告知客戶端響應(yīng)內(nèi)容為圖片形式鞭达,使用一種 叫做 MIME 類型的東西來(lái)
指定司忱。MIME 類型見 Tomcat 的 web.xml 文件。
//設(shè)置響應(yīng)類型
response.setContentType("image/jpeg");
//得到圖片的響應(yīng)路徑
String path=request.getServletContext().getRealPath("/image/s.jpg");
//通過(guò)路徑得到文件對(duì)象
File file=new File(path);
//得到字節(jié)輸出流
OutputStream os=response.getOutputStream();
//得到文件對(duì)象的輸入流
InputStream is=new FileInputStream(file);
//循環(huán)讀取
byte[] arr=new byte[1024];
int len=0;
while((len=is.read(arr))!=-1){
os.write(arr);
}
os.flush();
is.close();
os.close();
5畴蹭、重定向
重定向是一種服務(wù)器指導(dǎo)坦仍,客戶端的行為。
客戶端發(fā)出第一個(gè)請(qǐng)求叨襟,被服務(wù)器接收繁扎,經(jīng)過(guò)處理服務(wù)器進(jìn)行響應(yīng)。
與此同時(shí)糊闽,服務(wù)器給客戶端一個(gè)地址(下次請(qǐng)求的地址 resp.sendRedirect("url");)梳玫。
當(dāng)客戶端接收到響應(yīng)后,立刻右犹、馬上提澎、自動(dòng)根據(jù)服務(wù)器 給的地址進(jìn)行請(qǐng)求的發(fā)送第二個(gè)請(qǐng)求。
服務(wù)器接收請(qǐng)求并作出 響應(yīng)念链,重定向完成盼忌。
從描述中可以看出重定向當(dāng)中有兩個(gè)請(qǐng)求存在,并且屬于客戶端行為掂墓。
請(qǐng)求轉(zhuǎn)發(fā)和重定向的區(qū)別
1谦纱、請(qǐng)求轉(zhuǎn)發(fā)是服務(wù)端跳轉(zhuǎn),重定向是客戶端跳轉(zhuǎn)
2君编、請(qǐng)求轉(zhuǎn)發(fā)只有一次請(qǐng)求服协,重定向有兩次請(qǐng)求
3、請(qǐng)求轉(zhuǎn)發(fā)地址欄不發(fā)生改變啦粹,重定向地址欄會(huì)改變
4偿荷、請(qǐng)求轉(zhuǎn)發(fā)request域?qū)ο竽芄蚕砭接危囟ㄏ虿荒芄蚕? 5、請(qǐng)求轉(zhuǎn)發(fā)只能請(qǐng)求當(dāng)前資源下的路徑跳纳,重定向可以任意地址
response.sendRedirect("http://www.baidu.com");
6忍饰、請(qǐng)求路徑問題
路徑分為相對(duì) 路徑和絕對(duì)路徑
* 絕對(duì)路徑
可簡(jiǎn)單理解為完整路徑,在 web 項(xiàng)目中絕對(duì)路徑分 兩種寺庄,一種是以 http://開頭的艾蓝,該種絕對(duì)路徑已經(jīng)跨域,即任何地方的資源都能 訪問斗塘,另一種則是從當(dāng)前域名|IP|主機(jī)后的端口號(hào)開始的赢织,不能跨域,也屬于一 種絕對(duì)路徑馍盟。
* 相對(duì)路徑
相對(duì)路徑則就是相對(duì)當(dāng)前資源所在路徑于置。
* 總結(jié):
請(qǐng)求轉(zhuǎn)發(fā):
帶"/"的絕對(duì)路徑,定位的位置是在站點(diǎn)名之后
重定向:
帶"/"的絕對(duì)路徑贞岭,定位的位置是在端口之后
http://loaclhsot:8080/s01/ser01
注:ser01和ser02s是兩個(gè)同級(jí)的servlet
相對(duì)路徑
相對(duì)于的當(dāng)前資源所在路徑 ser02(重定向) ser02(請(qǐng)求轉(zhuǎn)發(fā))
絕對(duì)路徑
完整路徑
1八毯、以http://開頭的路徑,已經(jīng)跨域瞄桨,可以訪問任意地址
http://loaclhsot:8080/s01/ser02 (重定向) (請(qǐng)求轉(zhuǎn)發(fā)不可以)
2话速、以/開頭,定位在端口之后
/s01/ser02(重定向)
/ser02(在請(qǐng)求轉(zhuǎn)發(fā)時(shí)芯侥,"/"代表的含義是從http開始到站點(diǎn)名后)