Servlet學習筆記(一)
阿新 • • 發佈:2019-01-31
對java web學習的記錄,有關的一些教學視訊和原始碼可以加qq 1269557707 諮詢,如果對本教程滿意的話給個贊,留個好評吧,歡迎轉載,但請註明出處!
目錄:
下面是正文:
-------------------------------------------------------------------------------------------------------------------------------
什麼是Servlet?
Servlet 並不難理解,其實就是一個java應用程式,但是它和普通的java程式不同,它是由開發者建立,但是是由Servlet容器(Tomcat)執行的,它不能自動執行。Servlet容器將使用者的請求傳給Servlet應用程式,並把Servlet應用程式的響應回傳給使用者- Servlet是JavaWeb三大元件之一(Servlet、Filter、Listener)
- Servlet是用來處理客戶端請求的動態資源
- Servlet的任務有:
獲取請求資料處理請求完成響應 - Servlet介面方法:
void init(ServletConfig)void service(ServletRequest,ServletResponse)void destory()ServletConfig getServletConfig()String getServletInfo()
實現Servlet的方法
- 實現Servlet介面(不方便)
- 繼承GenericServlet類(不方便)
- 繼承HttpServlet類(方便)
Servlet API
Servlet生命週期
Servlet介面一共5個方法,但其中只有三個是生命週期方法:- void init(ServletConfig)
- void service(ServletRequest,ServletResponse)
- void destory()
- 當Servlet第一次被請求時,或伺服器啟動時,伺服器會建立Servlet例項。
- 伺服器預設是在servlet第一次被請求時建立Servlet例項,如果希望伺服器啟動時就建立Servlet實現需要在web.xml中配置
- 伺服器只為一個型別的Servlet建立一個例項物件,所以Servlet是單例的;
- 當伺服器建立Servlet例項後會馬上呼叫Servlet的init(ServletConfig)方法,完成對Servlet的初始化;
- init(ServletConfig)只會被呼叫一次
- 伺服器會在呼叫init()方法時傳遞ServletConfig引數
- 當Servlet被請求時,伺服器會呼叫Servlet的service(ServletRequest,ServletResponse)方法
- service(ServletRequest,ServletResponse)方法每處理一次請求,就會被呼叫一次,所以它可能會被呼叫N次
- 因為Servlet是單例的,所以可能在同一時刻一個Servlet物件會被多個請求同時訪問,所以這可能出現執行緒案例問題
- Servlet不是執行緒案例的,這有助與提高效率,但不能讓Servlet具有狀態,以免多個執行緒爭搶資料
- 伺服器通常不會銷燬Servlet,通常只有在伺服器關閉時才會銷燬Servlet
- 伺服器會在銷燬Servlet之前呼叫Servlet的destory()方法
- 可以在destory()方法中給出釋放Servlet佔有的資源,但通常Servlet是沒什麼可要釋放的,所以該方法一般都是空的
ServletConfig
ServletConfig是Servlet中的init()方法的引數型別,伺服器會在呼叫init()方法時傳遞ServletConfig物件給init()方法。ServletConfig物件封裝了Servlet在web.xml中的配置資訊,它對應< servlet >元素。ServletConfig類的功能有:- String getServletName():獲取Servlet配置名,即< servlet-name >的值;
- ServletContext getServletContext():獲取ServletContext物件,這個物件稍後介紹
- String getInitParameter(String name):獲取初始化引數
- Enumeration getInitParameterNames():獲取所有初始化引數的名稱
GenericServlet
GenericServlet是Servlet介面的實現類,但它是一個抽象類,它唯一的抽象方法就是service()方法GenericServlet實現了Servlet介面:- 實現了String getServletInfo()方法
- 實現了void destory()方法,空實現
- 實現了void init(ServletConfig)方法,用來儲存ServletConfig引數
- 實現了ServletConfig getServletConfig()方法
- 實現了ServletContext getServletContext()方法
- 實現了String getInitParameter()方法
- 實現了String getServletName()方法
- 實現了Enumeration getInitParameterNames()方法
- 該方法會被init(ServletConfig)方法呼叫
- 如果希望對Servlet進行初始化,那麼應該覆蓋init()方法,而不是init(ServletConfig)方法
HttpServlet
HttpServlet是GenericServlet的子類,它專注HTTP請求HttpServlet類的方法:- 實現了void service(ServletRequest,ServletResponse)方法,實現內容是:
- 把ServletRequest強轉成HttpServletRequest
- 把ServletResponse強轉成HttpServletResponse
- 呼叫本類新增的void service(HttpServletRequest,HttpServletResponse)方法
- 添加了void service(HttpServletRequest,HttpServletResponse)方法,內容是:
- 呼叫request的getMethod()獲取請求方式
- 如果請求方式為GET,那麼呼叫本類新增的doGet(HttpServletRequest,HttpServletResponse)方法
- 如果請求方式為POST,那麼呼叫本類新增的doPost(HttpServletRequest,HttpServletResponse)方法
- 添加了doGet(HttpServletRequest,HttpServletResponse)方法,內容是響應405,表示錯誤,所以我們應該去覆蓋這個方法
- 添加了doPost(HttpServletRequest,HttpServletResponse)方法,內容是響應405,表示錯誤,所以我們應用去覆蓋這個方法
- 不要去覆蓋void service(ServletRequest,ServletResponse)
- 不要去覆蓋void service(HttpServletRequest, HttpServletResponse)
- 而應該去覆蓋doGet()或doPost()方法。
< url-pattern >
< url-pattern >是的子元素,用來繫結Servlet的訪問路徑可以在一箇中給出多個< url-pattern >,也就是說一個Servlet可以有多個訪問路徑:<servlet-mapping><servlet-name>xxx</servlet-name><url-pattern>/helo1<url-pattern><url-pattern>/hello2<url-pattern></servlet-mapping>還可以在中使用萬用字元,即“*”。- < url-pattern > /* < url-pattern >:表示匹配任何路徑
- < url-pattern >/do/*< url-pattern >:表示匹配以/do開頭的任何路徑
- < url-pattern >*.do< url-pattern >:表示匹配任何以“.do”結尾的路徑
- 萬用字元要麼在開頭,要麼在結尾,不能在中間,例如:/*.do就是錯誤的使用。
- 如果不使用萬用字元,那麼< url-pattern >必須以“/”開頭,例如:< url-pattern >abc< /url-pattern >就是錯誤的
ServletContext
ServletContext是Servlet三大域物件之一ServletContext在伺服器啟動時建立,在伺服器關閉時銷燬,一個JavaWeb應用只建立一個ServletContext物件它的功能分類:- 存取資料
- 讀取web.xml中的應用初始化引數
- 讀取應用資源
- 獲取ServletContext物件
在HttpServlet中可以通過以下方法來獲取ServletContext物件
- ServletContext sc = this.getServletContext()
- ServletContext sc = this.getServletConfig().getServletContext()
- 存取資料
因為在一個JavaWeb應用中,只有一個ServletContext物件,所以在ServletContext中儲存的資料可以共整個JavaWeb應用中的動態資源共享
ServletContext是Servlet三大域物件之一,域物件內部有一個Map,用來儲存資料
- void setAttribute(String name, Object value):用來新增或替換ServletContext域資料
servletContext.setAttribute("xxx", "XXX"),新增域資料servletContext.setAttribute("xxx", "XXXX"),覆蓋域資料,因為在域中已經存在了名為xxx的資料,所以這次就是覆蓋了 - Object getAttribute(String name):通過名稱來獲取域資料
- void removeAttribute(String name):通過名稱移除域資料
- Enumeration getAttributeNames():獲取所有ServletContext域資料的名稱
- 靜態資源:html、css、javascript、圖片等;
- 動態資源:javaweb的動態資源有jsp/servlet,其他web程式動態資源有asp、php等。
- 讀取web.xml中配置的應用初始化引數
- servletContext.getInitParameter("p1"),返回v1
- servletContext.getInitParameter("p2"),返回v2
- servletContext.getInitParameterNames(),返回Enumeration,包含p1和p2
- 獲取專案資源
- String getRealPath(String path):獲取資源的真實名稱
String path = servletContext.getRealPath("/WEB-INF/a.jpg");
返回值為/WEB-INF/a.jpg真實路徑,即磁碟路徑:C:/tomcat6/wabapps/hello/WEB-INF/a.jpg - InputStream getResourceAsStream(String path):獲取資源的輸入流
InputStream in = servletContext.getResourceAsStream("/WEB-INF/a.jpg");
返回的是a.jpg的輸入流物件,可以從流中得到a.jpg的資料 - Set getResourcePaths(String path):獲取指定目錄下的所有資源路徑
Set paths = servletContext.getResourcePaths("/WEB-INF");
返回的Set中包含如下字串:
/WEB-INF/lib//WEB-INF/classes//WEB-INF/web.xml/WEB-INF/a.jpg
- 獲取類路徑資源
request和response
- 當伺服器接收到請求後,伺服器會建立request和response物件,把請求資料封裝到request物件中;
- 然後呼叫Servlet的service()方法時把這兩個物件傳遞給service()方法;
- 在service()方法中可以通過request物件獲取請求資料,可以使用response物件向客戶端完成響應;
- 每次請求伺服器都會建立新的request和response物件,即每個請求有自己獨自的request和response物件。
HttpServletRequest
HttpServletRequest表示HTTP環境中的Servlet請求常用方法:- Cookie[] getCookies() 返回一個Cookie陣列
- java.lang.String getContextPath() 返回表示請求Context的URI部分
- java.lang.String getHeader(java.lang.String name) 返回指定的HTTP請求頭
- HttpSession getSession() 返回與這個請求有關的session物件,如果沒有就新建一個
- 獲取請求頭
- 獲取請求引數
- Servlet三大域物件之一
- 請求包含和請求轉發
1.request域方法
- void setAttribute(String name, Object value):新增或替換request域屬性
- Object getAttribute(String name):獲取request域指定名稱的域屬性
- void removeAttribute(String name):移除request域指定名稱的域屬性
- Enumeration getAttributeNames():獲取所有request域的屬性名稱
2. request獲取請求頭
- String getHeader(String name):獲取指定名稱的請求頭
- int getIntHeader(String name):獲取指定名稱的請求頭,把值轉換成int型別。
- Enumeration getHeaderNames():獲取所有請求頭名稱
3.request請求資料相關其他方法
重點:- String getMethod():獲取請求方式
- String getContextPath():獲取上下文路徑,即“/” + 應用名稱,例如:/day05_1
- void setCharacterEncoding(String):設定請求體的編碼
- String getRemoteAddr():獲取客戶端IP地址
- int getContentLength():獲取請求體位元組數
- Locale getLocale():獲取請求Locale,例如zh_CN表示中文,中國
- String getCharacterEncoding():獲取請求體編碼,在沒有呼叫setCharacterEncoding()之前該方法返回null
- String getQueryString():獲取引數列表,例如:username=zhangSan&password=123
- String getRequestURI():返回請求URI路徑,從應用名稱開始,到引數之前這一段,例如:/day05_1/AServlet
- StringBuffer getRequestURL():整個請求URL,不包含引數部分
- String getServletPath():返回Servlet路徑,從應用名稱後開始,到引數之前這一段,不包含應用名稱。
- String getServerName():返回主機名,例如:localhost
- int getServerPort():返回伺服器埠號,例如:8080
4. 請求引數
獲取請求引數,即獲取超連結上的引數和表單中的引數- String getParameter(String name):獲取指定名稱的引數,如果存在同名引數,那麼該方法只獲取第一個引數值
- String[] getParameterValues(String name):獲取指定名稱的引數,因為同名引數的存在,所以返回值為String[]
- Enumeration getParameterNames():獲取所有引數名稱
- Map getParameterMap():獲取所有引數,封裝到Map中,key為引數名稱,value為引數值。
5. 請求包含和請求轉發
有時一個請求需要多個Servlet協作才能完成,所以需要在一個Servlet跳到另一個Servlet!- 請求包含和請求轉發都是在一個請求中,訪問兩個Servlet。
- 請求包含和請求轉發都是有一個Servlet去呼叫執行另一個Servlet
- 請求包含和請求轉發都可以共享request中的資料,因為都是一個請求。
一個請求跨多個Servlet,需要使用轉發和包含。請求轉發:由下一個Servlet完成響應體!當前Servlet可以設定響應頭!(留頭不留體)請求包含:由兩個Servlet共同未完成響應體!(都留)無論是請求轉發還是請求包含,都在一個請求範圍內!使用同一個request和response!eg:
- 從AServlet請求轉發到BServlet
在AServlet中可以設定響應頭在AServlet中不能使用響應流輸出
- 如果在AServlet中響應的資料導致response提交,那麼在轉發時丟擲異常;
- 如果在AServlet中響應的資料沒有導致response提交,那麼response中的資料會被清空。
- 從AServlet請求包含BServlet
在AServlet可以設定響應頭在AServlet可以使用響應流輸出 - 請求轉發和請求包含都要使用RequestDispatcher物件:RequestDispatcher rd = request.getRequestDispatcher("/BServlet");使用request獲取RequestDispatcher物件,方法的引數是被轉發或被包含的Servlet路徑
- 請求轉發執行RequestDispatcher的forward()方法:rd.forward(request,response);
- 請求包含執行RequestDispatcher的include()方法:rd.include(request,response);
- 請求轉發和請求包含的路徑都是伺服器端路徑,相對當前應用
6. 請求轉發與重定向
重定向
1)請求轉發是一個請求一次響應,而重定向是兩次請求兩次響應2)請求轉發位址列不變化,而重定向會顯示後一個請求的地址3)請求轉發只能轉發到本專案其他Servlet,而重定向不只能重定向到本專案的其他Servlet,還能定向到其他專案4)請求轉發是伺服器端行為,只需給出轉發的Servlet路徑,而重定向需要給出requestURI,即包含專案名!5)請求轉發和重定向效率是轉發高!因為是一個請求!如何選擇? 需要位址列發生變化,那麼必須使用重定向! 需要在下一個Servlet中獲取request域中的資料,必須要使用轉發!
7.request域
Servlet中三大域物件:request、session、application,都有如下三個方法:1)void setAttribute(String name, Object value)2)Object getAttribute(String name)3)void removeAttribute(String name);同一請求範圍內使用request.setAttribute()、request.getAttribute()來傳值!前一個Servlet呼叫setAttribute()儲存值,後一個Servlet呼叫getAttribute()獲取值。
9.請求引數和request域的不同( request.getParameter()和request.getAttribute())
請求引數和request域
- getParameter()是獲取客戶端引數,它是從客戶端傳遞給伺服器的資料。
- getAttribute()是獲取伺服器端自己設定的資料,而不是客戶端的資料。
- request沒有setParameter()方法,不能自己設定引數,引數都由客戶端傳遞
- request有setAttribute()方法,在getAttribute()之前,需要先setAttribute()才能獲取到。
- getAttribute()和setAttribute()是用來在請求轉發和請求包含中的多個Servlet中共享資料。
HttpServletResponse
response是響應物件,用來在Servlet的service()方法中向客戶端響應資料常用方法:- void addCookie(Cookie cookie) 為響應物件新增Cookie
- void addHeader(java.lang.String name, java.lang.String value) 為響應物件新增標頭
- void sendRedirect(java.lang.String location) 傳送響應代號,把瀏覽器重定向到指定位置
- 設定響應頭
- 傳送狀態碼
- 設定響應正文
- 重定向
1.響應正文
- ServletOutputStream getOutputStream():用來向客戶端響應位元組資料;
- PrintWriter getWriter():用來向客戶端響應字元資料;
2.response字元編碼
- Tomcat響應資料預設使用ISO-8859-1
- 通常瀏覽器預設使用GBK編碼
- response.setCharacterEncoding("utf-8"); / /設定response.getWriter()的字元編碼
- 設定字元流編碼。等同與呼叫了response.setCharacterEncoding("utf-8");
- 設定Content-type響應頭,即通知瀏覽器響應資料的編碼為utf-8。
因為設定字元流的編碼為utf-8,所以響應給客戶端資料為utf-8編碼
因為設定了Content-type頭為utf-8,所以瀏覽器會使用utf-8來解析響應資料
沒有亂碼!
- 設定了字元流為gbk,所以響應給客戶端的資料為gbk
- 設定了Content-type頭為gbk,所以通知瀏覽器響應資料為gbk編碼
沒有亂碼!
3. response字元流緩衝區
response字元流緩衝區大小為8KB,當向字元流中寫入資料後,資料可能只在緩衝區中,而沒有傳送到瀏覽器。可以呼叫response.flushBuffer()或response.getWriter().flush()方法重新整理緩衝區,把資料傳送到瀏覽器。4.設定響應頭
response.setHeader("Content-type", "text/html;charset=utf-8");等同與response.setContentType("text/html;charset=utf-8");response.setHeader("Refresh", "5; URL=http://www.itcast.cn");5.指定狀態碼
response.setStatus(200):設定狀態碼為200response.sendError(404, “您要查詢的資源不存在”):設定狀態碼為404repsonse.sendError(500, “伺服器出氏了”):設定狀態碼為500在呼叫sendError()方法時,不只是設定了狀態碼,而且還會給瀏覽器一個顯示錯誤的頁面。6.重定向
重定向response.sendStatus(302);repsonse.setHeader("Location", "http://www.baidu.com");快捷的方法重定向:
- 重定向是兩次請求
- 重定向不侷限與當前應用,也可以是其他應用,例如重定向到百度
- 重定向響應碼為302,而且必須有Location響應頭
- 重定向與response響應流同時使用。
路徑
客戶端路徑和伺服器路徑
- 客戶端路徑和伺服器端路徑
- 客戶端路徑需要給出應用名稱,例如:/day05_1/AServlet
- 伺服器端路徑無需給出應用名稱,例如:/AServlet
- 客戶端路徑
1). 頁面中都是客戶端路徑:
- 超連結的href
- 表單的action
- 的src
2). 重定向也是客戶端路徑:response.sendRedirect("/day05_1/BServlet");
- 伺服器端路徑
- 請求轉發和請求包含
- ServletContext獲取資源等
對以上的解釋(有點廢話)
- web.xml中路徑,(叫它Servlet路徑!)
要麼以“ * ”開關,要麼為“ / ”開頭
- 轉發和包含路徑
以“/”開頭:相對當前專案路徑,例如:http://localhost:8080/專案名/ request.getRequestdispacher("BServlet").for...();不以“/”開頭:相對當前Servlet路徑。 request.getRequestdispacher("/BServlet").for...();,假如當前Servlet是:http://localhost:8080/專案名/servlet/AServlet, 就是http://localhost:8080/專案名/servlet/BServlet
- 重定向路徑(客戶端路徑)
> 以“ / ”開頭:相對當前主機,例如:http: //localhost:8080/, 所以需要自己手動新增專案名,例如;response.sendRedirect(" /day10_1/Bservlet"); - 頁面中超連結和表單路徑
> 與重定向相同,都是客戶端路徑!需要新增專案名
< form action="/day10_1/AServlet">< a href="/day10_/AServlet">< a href="AServlet" > ,如果不以“/”開頭,那麼相對當前頁面所在路徑。如果是http://localhost:8080/day10_1/html/form.html。 即:http://localhost:8080/day10_1/html/ASevlet建議使用以“/”開頭的路徑,即絕對路徑! * ServletContext獲取資源路徑 > 相對當前專案目錄,即當然index.jsp所在目錄 * ClassLoader獲取資源路徑 > 相對classes目錄 * Class獲取資源路徑 > 以“/”開頭相對classes目錄 > 不以“/”開頭相對當前.class檔案所在目錄。
編碼
常見字元編碼:iso-8859-1(不支援中文)、gb2312、gbk、gb18030(系統預設編碼,中國的國標碼)、utf-8(萬國碼,支援全世界的編碼,所以我們使用這個)亂碼解決方案
一、瀏覽器和伺服器之間傳輸亂碼- 請求編碼
- 客戶端傳送的資料編碼:由瀏覽器來決定:
1). 如果是在位址列中直接給出url,那麼一般都是預設為GBK,但這個可能不太大。
2). 如果是通過頁面上的表單或超連結發出請求,那麼由當前頁面的編碼來決定傳送的引數的編碼。 - 無論瀏覽器傳送過來的是什麼編碼的資料,Tomcat都預設使用ISO-8859-1來解碼
1). POST:可以使用request.setCharacterEncoding()方法來設定請求體資料的編碼,因為POST請求引數在請求體中,所以是可以設定編碼的。在使用request.getParameter()方法獲取引數之前,先使用request.setCharacterEncoding("UTF-8")方法來設定編碼即可。
2). GET:沒有方法可以設定它,因為引數在url中。所以使用request.getParameter()獲取到的資料一定是錯誤的使用了iso-8859-1解碼的。可以再使用iso-8859-1把字串轉回到byte[],再重新使用正確的編碼來解碼即可。
String s = request.getParameter("s");//使用iso-8859-1錯誤的解碼了
byte[] bytes = s.getBytes("iso-8859-1");//退回錯誤的解碼,讓字串通過iso-8859-1返回到位元組資料,即還原位元組資料
s = new String(bytes, "utf-8");//重新使用正確的utf-8來解碼。
- 使用過濾器
亂碼詳解(有點廢話)
- 響應編碼
* 當使用response.getWriter()來向客戶端傳送字元資料時,如果在之前沒有設定編碼,那麼預設使用iso,因為iso不支援中文,一定亂碼
* 在使用response.getWriter()之前可以使用response.setCharaceterEncoding()來設定字元流的編碼為gbk或utf-8,當然我們通常會選擇utf-8。這樣使用response.getWriter()傳送的字元就是使用utf-8編碼的。但還是會出現亂碼!因為瀏覽器並不知道伺服器傳送過來的是什麼編碼的資料!這時瀏覽器會使用gbk來解碼,所以亂碼!
* 在使用response.getWriter()之前可以使用response.setHeader("Content-type","text/html;charset=utf-8")來設定響應頭,通知瀏覽器伺服器這邊使用的是utf-8編碼,而且在呼叫setHeader()後,還會自動執行setCharacterEncding()方法。這樣瀏覽器會使用utf-8解碼,所以就不會亂碼了!
* setHeader("Content-Type", "text/html;charset=utf-8")的快捷方法是:setContentType("text/html;charset=utf-8)。 - 請求編碼
* 客戶端傳送給伺服器的請求引數是什麼編碼:
客戶端首先要開啟一個頁面,然後在頁面中提交表單或點選超連結!在請求這個頁面時,伺服器響應的編碼是什麼,那麼客戶端傳送請求時的編碼就是什麼。
* 伺服器端預設使用什麼編碼來解碼引數:
伺服器端預設使用ISO-8859-1來解碼!所以這一定會出現亂碼的!因為iso不支援中文!
* 請求編碼處理分為兩種:GET和POST:GET請求引數不在請求體中,而POST請求引數在請求體中,所以它們的處理方式是不同的!
* GET請求編碼處理:
> String username = new String(request.getParameter("iso-8859-1"), "utf-8");
> 在server.xml中配置URIEncoding=utf-8
* POST請求編碼處理:
> String username = new String(request.getParameter("iso-8859-1"), "utf-8");
> 在獲取引數之前呼叫request.setCharacterEncoding("utf-8"); - URL編碼
表單的型別:Content-Type: application/x-www-form-urlencoded,就是把中文轉換成%後面跟隨兩位的16進位制。
為什麼要用它:在客戶端和伺服器之間傳遞中文時需要把它轉換成網路適合的方式。
補充:Get請求和Post請求
最為常見的客戶端傳遞引數方式有兩種:- 瀏覽器位址列直接輸入:一定是GET請求;
- 超連結:一定是GET請求;
- 表單:可以是GET,也可以是POST,這取決與的method屬性值;
- 請求引數會在瀏覽器的位址列中顯示,所以不安全;
- 請求引數長度限制長度在1K之內;
- GET請求沒有請求體,無法通過request.setCharacterEncoding()來設定引數的編碼;
POST請求: - 請求引數不會顯示瀏覽器的位址列,相對安全;
- 請求引數長度沒有限制;