Javaweb基础——Servlet
目錄:
1.Servlet入門
2.我的第一個Servlet
3.繼承HttpServlet類的方法來實(shí)現(xiàn)Servlet
4.使用IDE直接創(chuàng)建Servlet程序
5.Servlet的繼承體系
6.ServletConfig類
7.ServletCotext類
8.Http協(xié)議
9. GenericServlet抽象類
10.HttpServletRequest
11.HttpServletResponse
12.請求重定向
1.Servlet入門
如何實(shí)現(xiàn)Servlet:Servlet技術(shù)的核心是Servlet,它是所有Servlet類必須直接或者間接實(shí)現(xiàn)的一個接口。在編寫實(shí)現(xiàn)Servlet的Servlet類時,直接實(shí)現(xiàn)它。在擴(kuò)展實(shí)現(xiàn)這個這個接口的類時,間接實(shí)現(xiàn)它。
Servlet接口定義了Servlet與servlet容器之間的契約。這個契約是:Servlet容器將Servlet類載入內(nèi)存,并產(chǎn)生Servlet實(shí)例和調(diào)用它具體的方法。但是要注意的是,在一個應(yīng)用程序中,每種Servlet類型只能有一個實(shí)例。
用戶請求致使Servlet容器調(diào)用Servlet的Service()方法,并傳入一個ServletRequest對象和一個ServletResponse對象。ServletRequest對象和ServletResponse對象都是由Servlet容器(例如TomCat)封裝好的,并不需要程序員去實(shí)現(xiàn),程序員可以直接使用這兩個對象。
ServletRequest中封裝了當(dāng)前的Http請求,因此,開發(fā)人員不必解析和操作原始的Http數(shù)據(jù)。ServletResponse表示當(dāng)前用戶的Http響應(yīng),程序員只需直接操作ServletResponse對象就能把響應(yīng)輕松的發(fā)回給用戶。
對于每一個應(yīng)用程序,Servlet容器還會創(chuàng)建一個ServletContext對象。這個對象中封裝了上下文(應(yīng)用程序)的環(huán)境詳情。每個應(yīng)用程序只有一個ServletContext。每個Servlet對象也都有一個封裝Servlet配置的ServletConfig對象。
2.我的第一個Servlet
servlet工作流程:
3.繼承HttpServlet類的方法來實(shí)現(xiàn)Servlet
HttpServlet要比GenericServlet強(qiáng)大,其實(shí)也是有道理的。HttpServlet是由GenericServlet抽象類擴(kuò)展而來的,HttpServlet抽象類的聲明如下所示:
public abstract class HttpServlet extends GenericServlet implements SerializableHttpServlet之所以運(yùn)用廣泛的另一個原因是現(xiàn)在大部分的應(yīng)用程序都要與HTTP結(jié)合起來使用。這意味著我們可以利用HTTP的特性完成更多更強(qiáng)大的任務(wù)。Javax。servlet.http包是Servlet API中的第二個包,其中包含了用于編寫Servlet應(yīng)用程序的類和接口。Javax.servlet.http中的許多類型都覆蓋了Javax.servlet中的類型。
HttpServlet抽象類是繼承于GenericServlet抽象類而來的。使用HttpServlet抽象類時,還需要借助分別代表Servlet請求和Servlet響應(yīng)的HttpServletRequest和HttpServletResponse對象。
HttpServletRequest接口擴(kuò)展于javax.servlet.ServletRequest接口,HttpServletResponse接口擴(kuò)展于javax.servlet.servletResponse接口。
public interface HttpServletRequest extends ServletRequest public interface HttpServletResponse extends ServletResponseHttpServlet抽象類覆蓋了GenericServlet抽象類中的Service( )方法,并且添加了一個自己獨(dú)有的Service(HttpServletRequest request,HttpServletResponse方法。
讓我們來具體的看一看HttpServlet抽象類是如何實(shí)現(xiàn)自己的service方法吧:
首先來看GenericServlet抽象類中是如何定義service方法的:
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;我們看到是一個抽象方法,也就是HttpServlet要自己去實(shí)現(xiàn)這個service方法,我們在看看HttpServlet是怎么覆蓋這個service方法的:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;try {request = (HttpServletRequest)req;response = (HttpServletResponse)res;} catch (ClassCastException var6) {throw new ServletException("non-HTTP request or response");}this.service(request, response); }我們發(fā)現(xiàn),HttpServlet中的service方法把接收到的ServletRequsest類型的對象轉(zhuǎn)換成了HttpServletRequest類型的對象,把ServletResponse類型的對象轉(zhuǎn)換成了HttpServletResponse類型的對象。之所以能夠這樣強(qiáng)制的轉(zhuǎn)換,是因為在調(diào)用Servlet的Service方法時,Servlet容器總會傳入一個HttpServletRequest對象和HttpServletResponse對象,預(yù)備使用HTTP。因此,轉(zhuǎn)換類型當(dāng)然不會出錯了。
轉(zhuǎn)換之后,service方法把兩個轉(zhuǎn)換后的對象傳入了另一個service方法,那么我們再來看看這個方法是如何實(shí)現(xiàn)的:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();long lastModified;if (method.equals("GET")) {lastModified = this.getLastModified(req);if (lastModified == -1L) {this.doGet(req, resp);} else {long ifModifiedSince = req.getDateHeader("If-Modified-Since");if (ifModifiedSince < lastModified) {this.maybeSetLastModified(resp, lastModified);this.doGet(req, resp);} else {resp.setStatus(304);}}} else if (method.equals("HEAD")) {lastModified = this.getLastModified(req);this.maybeSetLastModified(resp, lastModified);this.doHead(req, resp);} else if (method.equals("POST")) {this.doPost(req, resp);} else if (method.equals("PUT")) {this.doPut(req, resp);} else if (method.equals("DELETE")) {this.doDelete(req, resp);} else if (method.equals("OPTIONS")) {this.doOptions(req, resp);} else if (method.equals("TRACE")) {this.doTrace(req, resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[]{method};errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(501, errMsg);}}我們發(fā)現(xiàn),這個service方法的參數(shù)是HttpServletRequest對象和HttpServletResponse對象,剛好接收了上一個service方法傳過來的兩個對象。
接下來我們再看看service方法是如何工作的,我們會發(fā)現(xiàn)在service方法中還是沒有任何的服務(wù)邏輯,但是卻在解析HttpServletRequest中的方法參數(shù),并調(diào)用以下方法之一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete。這7種方法中,每一種方法都表示一個Http方法。doGet和doPost是最常用的。所以,如果我們需要實(shí)現(xiàn)具體的服務(wù)邏輯,不再需要覆蓋service方法了,只需要覆蓋doGet或者doPost就好了。
總之,HttpServlet有兩個特性是GenericServlet所不具備的:
1.不用覆蓋service方法,而是覆蓋doGet或者doPost方法。在少數(shù)情況,還會覆蓋其他的5個方法。
2.使用的是HttpServletRequest和HttpServletResponse對象。
a.html文件按照第一個Servlet進(jìn)行配置就行了
4.使用IDE直接創(chuàng)建Servlet程序
5.Servlet的繼承體系
6.ServletConfig類
實(shí)現(xiàn)Servlet程序,當(dāng)我們是以繼承了HttpServlet類,我們可以使用getServletConfig()方法來獲取當(dāng)前的Servlet對應(yīng)的ServleConfigt對象,但是當(dāng)我們直接實(shí)現(xiàn)了Servlet接口,就不能使用這個方法來獲取ServleConfigt對象,因為這個方法會返回null
當(dāng)我們使用繼承HttpConfig實(shí)現(xiàn)Servlet程序,在重寫init()方法的時候我們需要在重寫的第一行加上一句super.init(config),因為HttpConfig類是繼承了GenericServlet類,而GenericServlet類,而GenericServlet的init()方法又保存了Servlet的ServletConfig的對象(GenericServlet實(shí)現(xiàn)了Servlet接口)所以如果不使用super.init(config)就無法保存ServletConfig對象
7.ServletCotext類
ServletContext對象表示Servlet應(yīng)用程序。每個Web應(yīng)用程序都只有一個ServletContext對象。在將一個應(yīng)用程序同時部署到多個容器的分布式環(huán)境中,每臺Java虛擬機(jī)上的Web應(yīng)用都會有一個ServletContext對象。
通過在ServletConfig中調(diào)用getServletContext方法,也可以獲得ServletContext對象。
那么為什么要存在一個ServletContext對象呢?存在肯定是有它的道理,因為有了ServletContext對象,就可以共享從應(yīng)用程序中的所有資料處訪問到的信息,并且可以動態(tài)注冊Web對象。前者將對象保存在ServletContext中的一個內(nèi)部Map中。保存在ServletContext中的對象被稱作屬性。
ServletContext中的下列方法負(fù)責(zé)處理屬性:
Object getAttribute(String var1);Enumeration<String> getAttributeNames();void setAttribute(String var1, Object var2);void removeAttribute(String var1);ServletContext對象只能獲取<context-param>標(biāo)簽里邊的內(nèi)容不能獲取<init-param>標(biāo)簽里邊的內(nèi)容,<init-param>只能由ServletConfig對象獲取
我們通過ServletConfig對象獲取ServletContext對象,同時我們還可以通過getServletContext()直接獲取
ServletContext對象是在web工程啟動時創(chuàng)建,web工程停止時銷毀我們setAttribute()與getAttribute()都是建立在服務(wù)器銷毀銷毀前,而且保存的數(shù)據(jù)可以在不同的Servlet服務(wù)器端進(jìn)行獲取,有點(diǎn)全局變量的意思
以上四個選項:
update resources ---- 更新靜態(tài)的資源,比如html,js,css等 運(yùn)行模式和調(diào)試模式都是立即生效;
update classes and resources ---- 更新java,jsp和靜態(tài)資源( 1. java修改后,會被編譯成.class,然后覆蓋到target/kao文件夾下,IDE調(diào)試模式的情況下,立即生效。IDE運(yùn)行模式下,不立即生效,需要redeployed才可生效。jsp修改后,再次被訪問的時候,會自動更新,重新編譯成java.class保存在tomcat的work目錄下。由于是訪問時才檢測是否修改,是否需要重新編譯,所以 IDE 運(yùn)行模式 和IDEA調(diào)試模式下,都是立即生效。刷新下頁面就可);
redeployed ----- 重新部署,發(fā)布到tomcat里,不重啟tomcat,而是把原來的刪掉,然后重新發(fā)布(此時的web工程未停止);
restart server ----- 重啟tomcat。最徹底!(此時的web工程停止)
8.Http協(xié)議
谷歌瀏覽器查看http協(xié)議:
9.GenericServlet抽象類
前面我們編寫Servlet一直是通過實(shí)現(xiàn)Servlet接口來編寫的,但是,使用這種方法,則必須要實(shí)現(xiàn)Servlet接口中定義的所有的方法,即使有一些方法中沒有任何東西也要去實(shí)現(xiàn),并且還需要自己手動的維護(hù)ServletConfig這個對象的引用。因此,這樣去實(shí)現(xiàn)Servlet是比較麻煩的。
void init(ServletConfig var1) throws ServletException;幸好,GenericServlet抽象類的出現(xiàn)很好的解決了這個問題。本著盡可能使代碼簡潔的原則,GenericServlet實(shí)現(xiàn)了Servlet和ServletConfig接口,下面是GenericServlet抽象類的具體代碼:
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {private static final String LSTRING_FILE = "javax.servlet.LocalStrings";private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");private transient ServletConfig config;public GenericServlet() {}public void destroy() {}public String getInitParameter(String name) {ServletConfig sc = this.getServletConfig();if (sc == null) {throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));} else {return sc.getInitParameter(name);}}public Enumeration<String> getInitParameterNames() {ServletConfig sc = this.getServletConfig();if (sc == null) {throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));} else {return sc.getInitParameterNames();}}public ServletConfig getServletConfig() {return this.config;}public ServletContext getServletContext() {ServletConfig sc = this.getServletConfig();if (sc == null) {throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));} else {return sc.getServletContext();}}public String getServletInfo() {return "";}public void init(ServletConfig config) throws ServletException {this.config = config;this.init();}public void init() throws ServletException {}public void log(String msg) {this.getServletContext().log(this.getServletName() + ": " + msg);}public void log(String message, Throwable t) {this.getServletContext().log(this.getServletName() + ": " + message, t);}public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;public String getServletName() {ServletConfig sc = this.getServletConfig();if (sc == null) {throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));} else {return sc.getServletName();}} }其中,GenericServlet抽象類相比于直接實(shí)現(xiàn)Servlet接口,有以下幾個好處:
1.為Servlet接口中的所有方法提供了默認(rèn)的實(shí)現(xiàn),則程序員需要什么就直接改什么,不再需要把所有的方法都自己實(shí)現(xiàn)了。
2.提供方法,包圍ServletConfig對象中的方法。
3.將init( )方法中的ServletConfig參數(shù)賦給了一個內(nèi)部的ServletConfig引用從而來保存ServletConfig對象,不需要程序員自己去維護(hù)ServletConfig了。
但是,我們發(fā)現(xiàn)在GenericServlet抽象類中還存在著另一個沒有任何參數(shù)的Init()方法:
public void init() throws ServletException { }設(shè)計者的初衷到底是為了什么呢?在第一個帶參數(shù)的init()方法中就已經(jīng)把ServletConfig對象傳入并且通過引用保存好了,完成了Servlet的初始化過程,那么為什么后面還要加上一個不帶任何參數(shù)的init()方法呢?這不是多此一舉嗎?
當(dāng)然不是多此一舉了,存在必然有存在它的道理。我們知道,抽象類是無法直接產(chǎn)生實(shí)例的,需要另一個類去繼承這個抽象類,那么就會發(fā)生方法覆蓋的問題,如果在類中覆蓋了GenericServlet抽象類的init()方法,那么程序員就必須手動的去維護(hù)ServletConfig對象了,還得調(diào)用super.init(servletConfig)方法去調(diào)用父類GenericServlet的初始化方法來保存ServletConfig對象,這樣會給程序員帶來很大的麻煩。GenericServlet提供的第二個不帶參數(shù)的init( )方法,就是為了解決上述問題的。
這個不帶參數(shù)的init()方法,是在ServletConfig對象被賦給ServletConfig引用后,由第一個帶參數(shù)的init(ServletConfig servletconfig)方法調(diào)用的,那么這意味著,當(dāng)程序員如果需要覆蓋這個GenericServlet的初始化方法,則只需要覆蓋那個不帶參數(shù)的init( )方法就好了,此時,servletConfig對象仍然有GenericServlet保存著。
說了這么多,通過擴(kuò)展GenericServlet抽象類,就不需要覆蓋沒有計劃改變的方法。因此,代碼將會變得更加的簡潔,程序員的工作也會減少很多。
10.HttpServletRequest
因為Request代表請求,所以我們可以通過該對象分別獲得HTTP請求的請求行,請求頭和請求體。
在前面我們講過,在service中使用的編碼解碼方式默認(rèn)為:ISO-8859-1編碼,但此編碼并不支持中文,因此會出現(xiàn)亂碼問題,所以我們需要手動修改編碼方式為UTF-8編碼,才能解決中文亂碼問題,下面是發(fā)生亂碼的具體細(xì)節(jié):
解決post提交方式的亂碼:request.setCharacterEncoding("UTF-8");解決get提交的方式的亂碼:parameter = newString(parameter.getbytes("iso8859-1"),"utf-8");上圖在獲取請求參數(shù)之前調(diào)用的意思是,在設(shè)置請求體字符編碼之前不調(diào)用HttpServletRequest的get之類方法,避免發(fā)生亂碼
上圖的?username=wzg168只是一種格式一般都是代表請求參數(shù)一般寫法是...=...
base標(biāo)簽的作用:
上面兩張圖可以實(shí)現(xiàn)c.html和index.html之間的跳轉(zhuǎn),但是當(dāng)我們使用請求轉(zhuǎn)發(fā)進(jìn)行跳轉(zhuǎn)的時候(服務(wù)
器默認(rèn)打開index.html頁面),再點(diǎn)擊回到a.html的時候就會發(fā)現(xiàn)跳轉(zhuǎn)不回去,以上代碼執(zhí)行順序,服務(wù)器先默認(rèn)打開index.html頁面——》點(diǎn)擊請求轉(zhuǎn)發(fā)a/b/c.html——》c.html然后再點(diǎn)擊c.html的的跳回首頁就會發(fā)生錯誤,錯誤原因如下(含base標(biāo)簽作用):
我們只需要改進(jìn)c.html就行,如下:
11.HttpServletResponse
響應(yīng)的數(shù)據(jù)通過兩個流傳遞給客戶端, javax.servlet.ServletResponse接口表示一個Servlet響應(yīng),在調(diào)用Servlet的Service( )方法前,Servlet容器會先創(chuàng)建一個ServletResponse對象,并把它作為第二個參數(shù)傳給Service( )方法。ServletResponse隱藏了向瀏覽器發(fā)送響應(yīng)的復(fù)雜過程。
PrintWriter getWriter()
獲得字符流,通過字符流的write(String s)方法可以將字符串設(shè)置到response 緩沖區(qū)中,隨后Tomcat會將response緩沖區(qū)中的內(nèi)容組裝成Http響應(yīng)返回給瀏覽器端。
ServletOutputStream getOutputStream()
獲得字節(jié)流,通過該字節(jié)流的write(byte[] bytes)可以向response緩沖區(qū)中寫入字節(jié),再由Tomcat服務(wù)器將字節(jié)內(nèi)容組成Http響應(yīng)返回給瀏覽器。
其中的getWriter方法,它返回了一個可以向客戶端發(fā)送文本的的Java.io.PrintWriter對象。默認(rèn)情況下,PrintWriter對象使用ISO-8859-1編碼(該編碼在輸入中文時會發(fā)生亂碼)。
在向客戶端發(fā)送響應(yīng)時,大多數(shù)都是使用該對象向客戶端發(fā)送HTML。
還有一個方法也可以用來向瀏覽器發(fā)送數(shù)據(jù),它就是getOutputStream,從名字就可以看出這是一個二進(jìn)制流對象,因此這個方法是用來發(fā)送二進(jìn)制數(shù)據(jù)的。
在發(fā)送任何HTML之前,應(yīng)該先調(diào)用setContentType()方法,設(shè)置響應(yīng)的內(nèi)容類型,并將“text/html”作為一個參數(shù)傳入,這是在告訴瀏覽器響應(yīng)的內(nèi)容類型為HTML,需要以HTML的方法解釋響應(yīng)內(nèi)容而不是普通的文本,或者也可以加上“charset=UTF-8”改變響應(yīng)的編碼方式以防止發(fā)生中文亂碼現(xiàn)象。
當(dāng)我們write的數(shù)據(jù)是中文就可能出現(xiàn)亂碼,那么以下是解決中文亂碼的問題的方法
方法1:
方法2:
response工作流程
12.請求重定向
請求重定向第一種方法:
上圖的setHeader方法的name參數(shù)代表新的地址,value代表新的訪問地址,可以是工程外的資源比如百度等等
請求重定向第二種方法:
總結(jié)
以上是生活随笔為你收集整理的Javaweb基础——Servlet的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么IEE754标准中,32位浮点数的
- 下一篇: C++ STL 之 unordered_