一篇学会HttpServletRequest
前言
JavaWeb系列教程
JavaWeb—Servlet
模擬Servlet本質(zhì)
使用IDEA開發(fā)Servlet程序
Servlet對(duì)象的生命周期
適配器(GenericServlet)改造Servlet
ServletConfig
Servlet–ServletContext
HTTP協(xié)議,get和post的區(qū)別
web站點(diǎn)歡迎頁
一篇學(xué)會(huì)HttpServletRequest
如果大家覺得有幫助的話,不妨動(dòng)動(dòng)小手,點(diǎn)贊收藏一波,也方便后面的復(fù)習(xí)哈
在學(xué)習(xí)一個(gè)東西之前,我們首先應(yīng)該了解它是什么,接下來讓我們來認(rèn)識(shí)一下它吧。
初識(shí)HttpServletRequest
-
HttpServletRequest是一個(gè)接口,全限定名稱:jakarta.servlet.http.HttpServletRequest
-
HttpServletRequest接口是Servlet規(guī)范中的一員。
-
HttpServletRequest接口的父接口:ServletRequest
public interface HttpServletRequest extends ServletRequest {}
實(shí)現(xiàn)類
-
HttpServletRequest接口的實(shí)現(xiàn)類誰寫的? HttpServletRequest對(duì)象是誰給創(chuàng)建的?
-
通過測試: org.apache.catalina.connector.RequestFacade 實(shí)現(xiàn)了 HttpServletRequest接口
public class RequestFacade implements HttpServletRequest {}測試結(jié)果說明:Tomcat服務(wù)器(WEB服務(wù)器、WEB容器)實(shí)現(xiàn)了HttpServletRequest接口,還是說明了Tomcat服務(wù)器實(shí)現(xiàn)了Servlet規(guī)范。而對(duì)于我們javaweb程序員來說,實(shí)際上不需要關(guān)心這個(gè),我們只需要面向接口編程即可。我們關(guān)心的是HttpServletRequest接口中有哪些方法,這些方法可以完成什么功能!!!!
封裝的信息
-
HttpServletRequest對(duì)象中都有什么信息?都包裝了什么信息?
- HttpServletRequest對(duì)象是Tomcat服務(wù)器負(fù)責(zé)創(chuàng)建的。這個(gè)對(duì)象中封裝了什么信息? 封裝了HTTP的請(qǐng)求協(xié)議。
- 實(shí)際上是用戶發(fā)送請(qǐng)求的時(shí)候,遵循了HTTP協(xié)議,發(fā)送的是HTTP的請(qǐng)求協(xié)議,Tomcat服務(wù)器將HTTP協(xié)議中的信息以及數(shù)據(jù)全部解析出來,然后Tomcat服務(wù)器把這些信息封裝到HttpServletRequest對(duì)象當(dāng)中,傳給了我們javaweb程序員。
- javaweb程序員面向HttpServletRequest接口編程,調(diào)用方法就可以獲取到請(qǐng)求的信息了。
-
request和response對(duì)象的生命周期?
- request對(duì)象和response對(duì)象,一個(gè)是請(qǐng)求對(duì)象,一個(gè)是響應(yīng)對(duì)象。這兩個(gè)對(duì)象只在當(dāng)前請(qǐng)求中有效。
- 一次請(qǐng)求對(duì)應(yīng)一個(gè)request。
- 兩次請(qǐng)求則對(duì)應(yīng)兩個(gè)request。
- …
HttpServletRequest接口中常用的方法
-
怎么獲取前端瀏覽器用戶提交的數(shù)據(jù)?
Map<String,String[]> getParameterMap() 這個(gè)是獲取MapEnumeration<String> getParameterNames() 這個(gè)是獲取Map集合中所有的keyString[] getParameterValues(String name) 根據(jù)key獲取Map集合的valueString getParameter(String name) 獲取value這個(gè)一維數(shù)組當(dāng)中的第一個(gè)元素。這個(gè)方法最常用。以上的4個(gè)方法,和獲取用戶提交的數(shù)據(jù)有關(guān)系。下面是方法的測試
Enumeration<String> parameterNames = request.getParameterNames();while (parameterNames.hasMoreElements()){String s = parameterNames.nextElement();writer.print(s+"<br>");}
//獲取所有nameEnumeration<String> parameterNames = request.getParameterNames();while (parameterNames.hasMoreElements()){String s = parameterNames.nextElement();//通過name,獲取valueString[] parameterValues = request.getParameterValues(s);for (int i = 0; i < parameterValues.length; i++) {writer.print(s+"--->"+parameterValues[i]+"<br>");}}
思考:
為了幫助大家理解并且更好的記憶上面的方法,我們來思考一下,如果是我們自己,當(dāng)前端form表單提交了數(shù)據(jù)以后,我們打算怎么存儲(chǔ)數(shù)據(jù)
前端提交的數(shù)據(jù)格式:username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt
我會(huì)采用Map集合來存儲(chǔ):
Map<String,String>key存儲(chǔ)Stringvalue存儲(chǔ)String這種想法對(duì)嗎?不對(duì)。如果采用以上的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)會(huì)發(fā)現(xiàn)key重復(fù)的時(shí)候value覆蓋。key value---------------------username abcuserpwd 111aihao saihao daihao tt這樣是不行的,因?yàn)閙ap的key不能重復(fù)。
Map<String, String[]>key存儲(chǔ)Stringvalue存儲(chǔ)String[]key value-------------------------------username {"abc"}userpwd {"111"}aihao {"s","d","tt"}
注意:前端表單提交數(shù)據(jù)的時(shí)候,假設(shè)提交了120這樣的“數(shù)字”,其實(shí)是以字符串"120"的方式提交的, 所以服務(wù)器端獲取到的一定是一個(gè)字符串的"120",而不是一個(gè)數(shù)字。(前端永遠(yuǎn)提交的是字符串,后端獲取的也永遠(yuǎn)是字符串。)
請(qǐng)求域?qū)ο?/h1>
request對(duì)象實(shí)際上又稱為“請(qǐng)求域”對(duì)象。
我們可以把請(qǐng)求域?qū)ο蠛蛻?yīng)用域?qū)ο髮?duì)比學(xué)習(xí)
我在之前的博客已經(jīng)說過相關(guān)內(nèi)容了,大家可能忘記了
先來復(fù)習(xí)一下之前講過的應(yīng)用域?qū)ο?/p>
應(yīng)用域?qū)ο?/h2>
-
應(yīng)用域?qū)ο笫鞘裁?#xff1f;
-
ServletContext (Servlet上下文對(duì)象。)
-
什么情況下會(huì)考慮向ServletContext這個(gè)應(yīng)用域當(dāng)中綁定數(shù)據(jù)呢?
- 第一:所有用戶共享的數(shù)據(jù)。—假設(shè)現(xiàn)在有一張數(shù)據(jù)庫表,我們把查詢好的數(shù)據(jù)放在ServletContext中,那么以后如果我們還想要獲得這些數(shù)據(jù),就直接從ServletContext當(dāng)中去取出來,而不需要每次都去連接數(shù)據(jù)庫,去和數(shù)據(jù)庫交互,和數(shù)據(jù)庫的交互,需要使用IO流,大量的IO流,效率很低,IO讀的是硬盤,硬盤上有磁針,它再快也是機(jī)械型的行為,通常我們優(yōu)化系統(tǒng)的時(shí)候使用緩存機(jī)制
- 第二:這個(gè)共享的數(shù)據(jù)量很小。—ServletContext是應(yīng)用域,服務(wù)器啟動(dòng)的時(shí)候創(chuàng)建,服務(wù)器關(guān)閉的時(shí)候銷毀,它會(huì)一直占用內(nèi)存
- 第三:這個(gè)共享的數(shù)據(jù)很少的修改操作。—如果經(jīng)常修改,會(huì)有線程安全問題,為了避免這樣的問題出現(xiàn),就會(huì)使用鎖機(jī)制,這樣就導(dǎo)致效率降低
- 在以上三個(gè)條件都滿足的情況下,使用這個(gè)應(yīng)用域?qū)ο?#xff0c;可以大大提高我們程序執(zhí)行效率。
- 實(shí)際上向應(yīng)用域當(dāng)中綁定數(shù)據(jù),就相當(dāng)于把數(shù)據(jù)放到了緩存(Cache)當(dāng)中,然后用戶訪問的時(shí)候直接從緩存中取,減少IO的操作,大大提升系統(tǒng)的性能,所以緩存技術(shù)是提高系統(tǒng)性能的重要手段。
-
應(yīng)用域?qū)ο笫鞘裁?#xff1f;
-
ServletContext (Servlet上下文對(duì)象。)
-
什么情況下會(huì)考慮向ServletContext這個(gè)應(yīng)用域當(dāng)中綁定數(shù)據(jù)呢?
- 第一:所有用戶共享的數(shù)據(jù)。—假設(shè)現(xiàn)在有一張數(shù)據(jù)庫表,我們把查詢好的數(shù)據(jù)放在ServletContext中,那么以后如果我們還想要獲得這些數(shù)據(jù),就直接從ServletContext當(dāng)中去取出來,而不需要每次都去連接數(shù)據(jù)庫,去和數(shù)據(jù)庫交互,和數(shù)據(jù)庫的交互,需要使用IO流,大量的IO流,效率很低,IO讀的是硬盤,硬盤上有磁針,它再快也是機(jī)械型的行為,通常我們優(yōu)化系統(tǒng)的時(shí)候使用緩存機(jī)制
- 第二:這個(gè)共享的數(shù)據(jù)量很小。—ServletContext是應(yīng)用域,服務(wù)器啟動(dòng)的時(shí)候創(chuàng)建,服務(wù)器關(guān)閉的時(shí)候銷毀,它會(huì)一直占用內(nèi)存
- 第三:這個(gè)共享的數(shù)據(jù)很少的修改操作。—如果經(jīng)常修改,會(huì)有線程安全問題,為了避免這樣的問題出現(xiàn),就會(huì)使用鎖機(jī)制,這樣就導(dǎo)致效率降低
- 在以上三個(gè)條件都滿足的情況下,使用這個(gè)應(yīng)用域?qū)ο?#xff0c;可以大大提高我們程序執(zhí)行效率。
- 實(shí)際上向應(yīng)用域當(dāng)中綁定數(shù)據(jù),就相當(dāng)于把數(shù)據(jù)放到了緩存(Cache)當(dāng)中,然后用戶訪問的時(shí)候直接從緩存中取,減少IO的操作,大大提升系統(tǒng)的性能,所以緩存技術(shù)是提高系統(tǒng)性能的重要手段。
-
你見過哪些緩存技術(shù)呢?
- 字符串常量池
- 整數(shù)型常量池 [-128~127],但凡是在這個(gè)范圍當(dāng)中的Integer對(duì)象不再創(chuàng)建新對(duì)象,直接從這個(gè)整數(shù)型常量池中獲取。大大提升系統(tǒng)性能。
- 數(shù)據(jù)庫連接池(提前創(chuàng)建好N個(gè)連接對(duì)象,將連接對(duì)象放到集合當(dāng)中,使用連接對(duì)象的時(shí)候,直接從緩存中拿。省去了連接對(duì)象的創(chuàng)建過程。效率提升。)
- 線程池(Tomcat服務(wù)器就是支持多線程的。所謂的線程池就是提前先創(chuàng)建好N個(gè)線程對(duì)象,將線程對(duì)象存儲(chǔ)到集合中,然后用戶請(qǐng)求過來之后,直接從線程池中獲取線程對(duì)象,直接拿來用。提升系統(tǒng)性能)
- 后期你還會(huì)學(xué)習(xí)更多的緩存技術(shù),例如:redis、mongoDB…
- ServletContext當(dāng)中有三個(gè)操作域的方法:
void setAttribute(String name, Object obj); // 向域當(dāng)中綁定數(shù)據(jù)。Object getAttribute(String name); // 從域當(dāng)中根據(jù)name獲取數(shù)據(jù)。void removeAttribute(String name); // 將域當(dāng)中綁定的數(shù)據(jù)移除// 以上的操作類似于Map集合的操作。Map<String, Object> map;map.put("name", obj); // 向map集合中放key和valueObject obj = map.get("name"); // 通過map集合的key獲取valuemap.remove("name"); // 通過Map集合的key刪除key和value這個(gè)鍵值對(duì)。
請(qǐng)求域”對(duì)象
請(qǐng)求域”對(duì)象要比“應(yīng)用域”對(duì)象范圍小很多。生命周期短很多。請(qǐng)求域只在一次請(qǐng)求內(nèi)有效。
一個(gè)請(qǐng)求對(duì)象request對(duì)應(yīng)一個(gè)請(qǐng)求域?qū)ο蟆R淮握?qǐng)求結(jié)束之后,這個(gè)請(qǐng)求域就銷毀了。
執(zhí)行原理
請(qǐng)求域?qū)ο蠓椒?#xff1a;
void setAttribute(String name, Object obj); // 向域當(dāng)中綁定數(shù)據(jù)。Object getAttribute(String name); // 從域當(dāng)中根據(jù)name獲取數(shù)據(jù)。void removeAttribute(String name); // 將域當(dāng)中綁定的數(shù)據(jù)移除
請(qǐng)求域和應(yīng)用域的選用原則
盡量選擇使用小的域?qū)ο?#xff0c;這樣占用的資源比較少
轉(zhuǎn)發(fā)
第一步:獲取請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象
RequestDispatcher dispatcher = request.getRequestDispatcher("/b");第二步:調(diào)用轉(zhuǎn)發(fā)器的forward方法完成跳轉(zhuǎn)/轉(zhuǎn)發(fā)
dispatcher.forward(request,response);轉(zhuǎn)發(fā)是一次請(qǐng)求第一步和第二步代碼可以聯(lián)合在一起。
request.getRequestDispatcher("/b").forward(request,response);
兩個(gè)Servlet共享數(shù)據(jù)
-
將數(shù)據(jù)放到ServletContext應(yīng)用域當(dāng)中,當(dāng)然是可以的,但是應(yīng)用域范圍太大,占用資源太多。不建議使用。
-
可以將數(shù)據(jù)放到request域當(dāng)中,然后AServlet轉(zhuǎn)發(fā)到BServlet,保證AServlet和BServlet在同一次請(qǐng)求當(dāng)中,這樣就可以做到兩個(gè)Servlet,或者多個(gè)Servlet共享同一份數(shù)據(jù)。
-
只要是服務(wù)器的合法資源都可以轉(zhuǎn)發(fā),可以是html,servlet等
-
注意:轉(zhuǎn)發(fā)的時(shí)候,路徑的寫法要注意,轉(zhuǎn)發(fā)的路徑以“/”開始,不加項(xiàng)目名。
容易混淆的方法
uri?username=zhangsan&userpwd=123&sex=1
String username = request.getParameter(“username”);
之前一定是執(zhí)行過:request.setAttribute(“name”, new Object())
Object obj = request.getAttribute(“name”);
以上兩個(gè)方法的區(qū)別是什么
第一個(gè)方法:獲取的是用戶在瀏覽器上提交的數(shù)據(jù).
第二個(gè)方法:獲取的是請(qǐng)求域當(dāng)中綁定的數(shù)據(jù)。
其他方法和亂碼問題
其他方法
-
獲取客戶端的IP地址
String remoteAddr = request.getRemoteAddr(); -
獲取應(yīng)用的根路徑
String contextPath = request.getContextPath(); -
獲取請(qǐng)求方式
String method = request.getMethod(); -
獲取請(qǐng)求的URI
String uri = request.getRequestURI(); // /aaa/testRequest -
獲取servlet path
String servletPath = request.getServletPath(); // /testRequest
亂碼問題
有時(shí)候,我們程序?qū)懞靡院?#xff0c;滿心歡喜,開啟服務(wù)器,期待在瀏覽器上的運(yùn)行結(jié)果,結(jié)果卻出現(xiàn)了亂碼問題,那我們?cè)趺唇鉀Q呢
get請(qǐng)求在請(qǐng)求行上提交數(shù)據(jù)。
post請(qǐng)求在請(qǐng)求體中提交數(shù)據(jù)。
設(shè)置請(qǐng)求體的字符集。(顯然這個(gè)方法是處理POST請(qǐng)求的亂碼問題。這種方式并不能解決get請(qǐng)求的亂碼問題。)
Tomcat10之后,request請(qǐng)求體當(dāng)中的字符集默認(rèn)就是UTF-8,不需要設(shè)置字符集,不會(huì)出現(xiàn)亂碼問題。
Tomcat9前(包括9在內(nèi)),如果前端請(qǐng)求體提交的是中文,后端獲取之后出現(xiàn)亂碼,怎么解決這個(gè)亂碼?執(zhí)行以下代碼。
request.setCharacterEncoding(“UTF-8”);
在Tomcat9之前(包括9),響應(yīng)中文也是有亂碼的,怎么解決這個(gè)響應(yīng)的亂碼?
response.setContentType(“text/html;charset=UTF-8”);
在Tomcat10之后,包括10在內(nèi),響應(yīng)中文的時(shí)候就不在出現(xiàn)亂碼問題了。以上代碼就不需要設(shè)置UTF-8了。
注意一個(gè)細(xì)節(jié)
在Tomcat10包括10在內(nèi)之后的版本,中文將不再出現(xiàn)亂碼。(這也體現(xiàn)了中文地位的提升。)
get請(qǐng)求亂碼問題怎么解決?
get請(qǐng)求發(fā)送的時(shí)候,數(shù)據(jù)是在請(qǐng)求行上提交的,不是在請(qǐng)求體當(dāng)中提交的。
get請(qǐng)求亂碼怎么解決
方案:修改CATALINA_HOME/conf/server.xml配置文件
注意:從Tomcat8之后,URIEncoding的默認(rèn)值就是UTF-8,所以GET請(qǐng)求也沒有亂碼問題了。
轉(zhuǎn)發(fā)和重定向(重要)
在代碼上的區(qū)別
轉(zhuǎn)發(fā)
獲取請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象
RequestDispatcherdispatcher=request.getRequestDispatcher("/dept/list")
調(diào)用請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象的forward方法完成轉(zhuǎn)發(fā)
dispatcher.forward(request, response);
合并一行代碼
request.getRequestDispatcher("/dept/list").forward(request,
response);
轉(zhuǎn)發(fā)的時(shí)候是一次請(qǐng)求,不管你轉(zhuǎn)發(fā)了多少次。都是一次請(qǐng)求。
AServlet轉(zhuǎn)發(fā)到BServlet,再轉(zhuǎn)發(fā)到CServlet,再轉(zhuǎn)發(fā)到DServlet,不管轉(zhuǎn)發(fā)了多少次,都在同一個(gè)request當(dāng)中。
這是因?yàn)檎{(diào)用forward方法的時(shí)候,會(huì)將當(dāng)前的request和response對(duì)象傳遞給下一個(gè)Servlet。
重定向
注意:路徑上要加一個(gè)項(xiàng)目名。為什么?
瀏覽器發(fā)送請(qǐng)求,請(qǐng)求路徑上是需要添加項(xiàng)目名的。
以下這一行代碼會(huì)將請(qǐng)求路徑“/oa/dept/list”發(fā)送給瀏覽器
瀏覽器會(huì)自發(fā)的向服務(wù)器發(fā)送一次全新的請(qǐng)求:/oa/dept/list
response.sendRedirect("/oa/dept/list");
形式上的區(qū)別
轉(zhuǎn)發(fā)(一次請(qǐng)求)
在瀏覽器地址欄上發(fā)送的請(qǐng)求是:http://localhost:8080/servlet10/a ,最終請(qǐng)求結(jié)束之后,瀏覽器地址欄上的地址還是這個(gè)。沒變。
重定向(兩次請(qǐng)求)
- 在瀏覽器地址欄上發(fā)送的請(qǐng)求是:http://localhost:8080/servlet10/a ,最終在瀏覽器地址欄上顯示的地址是:http://localhost:8080/servlet10/b
- 在瀏覽器輸入:localhost:8080/servlet09/a結(jié)果直接跳轉(zhuǎn)到b
本質(zhì)區(qū)別
-
轉(zhuǎn)發(fā):是由WEB服務(wù)器來控制的。A資源跳轉(zhuǎn)到B資源,這個(gè)跳轉(zhuǎn)動(dòng)作是Tomcat服務(wù)器內(nèi)部完成的。
-
重定向:是瀏覽器完成的。具體跳轉(zhuǎn)到哪個(gè)資源,是瀏覽器說了算。
二者如何選擇
-
轉(zhuǎn)發(fā)和重定向應(yīng)該如何選擇?什么時(shí)候使用轉(zhuǎn)發(fā),什么時(shí)候使用重定向?
- 如果在上一個(gè)Servlet當(dāng)中向request域當(dāng)中綁定了數(shù)據(jù),希望從下一個(gè)Servlet當(dāng)中把request域里面的數(shù)據(jù)取出來,使用轉(zhuǎn)發(fā)機(jī)制。
- 剩下所有的請(qǐng)求均使用重定向。(重定向使用較多。)
-
跳轉(zhuǎn)的下一個(gè)資源有沒有要求呢?必須是一個(gè)Servlet嗎?
- 不一定,跳轉(zhuǎn)的資源只要是服務(wù)器內(nèi)部合法的資源即可。包括:Servlet、JSP、HTML…
-
轉(zhuǎn)發(fā)會(huì)存在瀏覽器的刷新問題。
比如說我們把數(shù)據(jù)保存到數(shù)據(jù)庫中,希望跳轉(zhuǎn)到成功頁面,當(dāng)我們使用轉(zhuǎn)發(fā)的時(shí)候,只要瀏覽器刷新一次,數(shù)據(jù)庫就會(huì)多出一條數(shù)據(jù),這樣是不可取的,所以可以使用重定向
這里推薦一篇關(guān)于轉(zhuǎn)發(fā)和重定向的文章:轉(zhuǎn)發(fā)和重定向(完整理解及總結(jié))
總結(jié)
以上是生活随笔為你收集整理的一篇学会HttpServletRequest的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Servlet----ServletCo
- 下一篇: web站点的欢迎页面