servlet
該筆記來(lái)源于 黑馬傳智2020最新 以及 傳智Javaweb ,此筆記是我將兩者的綜合而成
servlet的概述
什么是servlet及皂?
Servlet是JavaWeb的<font color="red">三大組件</font>>之一,它屬于動(dòng)態(tài)資源爽雄。Servlet的作用是處理請(qǐng)求窗轩,服務(wù)器會(huì)把接收到的請(qǐng)求交給Servlet來(lái)處理,在Servlet中通常需要:
- 接收請(qǐng)求數(shù)據(jù);
- 處理請(qǐng)求
- 完成響應(yīng)
例如客戶端發(fā)出登錄請(qǐng)求难咕,或者輸出注冊(cè)請(qǐng)求,這些請(qǐng)求都應(yīng)該由Servlet來(lái)完成處理距辆!Servlet需要我們自己來(lái)編寫(xiě)余佃,每個(gè)Servlet必須實(shí)現(xiàn)javax.servlet.Servlet接口。
Servlet帶來(lái)的最大作用就是能夠處理瀏覽器帶來(lái)的HTTP請(qǐng)求跨算,并返回一個(gè)響應(yīng)給瀏覽器爆土,從而實(shí)現(xiàn)瀏覽器和服務(wù)器的交互。
形象化:
客戶端:我想吃西紅柿炒雞蛋
服務(wù)器:客戶想吃西紅柿炒雞蛋诸蚕。
servlet:服務(wù)器說(shuō)客戶端想吃西紅柿炒雞蛋(request)
service:做西紅柿炒雞蛋
servlet:做好了雾消,服務(wù)器快接著(response)
客戶端:怎么那么慢?
實(shí)現(xiàn)servlet的方式
實(shí)現(xiàn)Servlet有三種方式:
- 實(shí)現(xiàn)javax.servlet.Servlet接口挫望;
- 繼承javax.servlet.GenericServlet類(lèi)立润;
- 繼承javax.servlet.http.HttpServlet類(lèi);
通常我們會(huì)去繼承HttpServlet類(lèi)來(lái)完成我們的Servlet媳板,但學(xué)習(xí)Servlet還要從javax.servlet.Servlet接口開(kāi)始學(xué)習(xí)桑腮。
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
Servlet中的方法大多數(shù)不由我們來(lái)調(diào)用,而是由Tomcat來(lái)調(diào)用蛉幸。并且Servlet的對(duì)象也不由我們來(lái)創(chuàng)建破讨,由Tomcat來(lái)創(chuàng)建!
創(chuàng)建helloservlet應(yīng)用
首先在webapps目錄下創(chuàng)建helloservlet目錄奕纫,它就是我們的應(yīng)用目錄了提陶,然后在helloservlet目錄中創(chuàng)建準(zhǔn)備JavaWeb應(yīng)用所需內(nèi)容:
- 創(chuàng)建/helloservlet/WEB-INF目錄;
- 創(chuàng)建/helloservlet/WEB-INF/classes目錄匹层;
- 創(chuàng)建/helloservlet/WEB-INF/lib目錄隙笆;
- 創(chuàng)建/helloservlet/WEB-INF/web.xml文件;
完成Servlet需要分為兩步:
- 編寫(xiě)Servlet類(lèi);
- 在web.xml文件中配置Servlet撑柔;
HelloServlet.java
public class HelloServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {}
public ServletConfig getServletConfig() {return null;}
public void destroy() {}
public String getServletInfo() {return null;}
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
System.out.println("hello servlet!");
}
}
暫時(shí)忽略Servlet中其他四個(gè)方法瘸爽,只關(guān)心service()方法,因?yàn)樗怯脕?lái)處理請(qǐng)求的方法铅忿。我們?cè)谠摲椒▋?nèi)給出一條輸出語(yǔ)句剪决!
web.xml
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>cn.itcast.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/helloworld</url-pattern>
</servlet-mapping>
在web.xml中配置Servlet的目的是把訪問(wèn)路徑與一個(gè)Servlet綁定到一起,上面配置是把訪問(wèn)路徑:“/helloworld”與“cn.itcast.servlet.HelloServlet”綁定到一起檀训。
- <servlet>:指定HelloServlet這個(gè)Servlet的名稱(chēng)為hello
- <servlet-mapping>:指定/helloworld訪問(wèn)路徑所以訪問(wèn)的Servlet名為hello柑潦。
<servlet>和<servlet-mapping>通過(guò)<servlet-name>這個(gè)元素關(guān)聯(lián)在一起了!
- <url-pattern>是<servlet-mapping>的子元素峻凫,用來(lái)指定Servlet的訪問(wèn)路徑渗鬼,即URL。它必須是以“/”開(kāi)頭蔚晨!
servlet接口
servlet的生命周期
所謂xxx的生命周期乍钻,就是說(shuō)xxx的出生、服務(wù)铭腕,以及死亡银择。Servlet生命周期也是如此!與Servlet的生命周期相關(guān)的方法有:
- void init(ServletConfig)累舷;
- void service(ServletRequest,ServletResponse)浩考;
- void destroy();
servlet的出生
服務(wù)器會(huì)在Servlet第一次被訪問(wèn)時(shí)創(chuàng)建Servlet被盈,或者是在服務(wù)器啟動(dòng)時(shí)創(chuàng)建Servlet析孽。如果服務(wù)器啟動(dòng)時(shí)就創(chuàng)建Servlet,那么還需要在web.xml文件中配置只怎。也就是說(shuō)默認(rèn)情況下袜瞬,Servlet是在第一次被訪問(wèn)時(shí)由服務(wù)器創(chuàng)建的。
而且一個(gè)Servlet類(lèi)型身堡,服務(wù)器只創(chuàng)建一個(gè)實(shí)例對(duì)象邓尤,例如在我們首次訪問(wèn)http://localhost:8080/helloservlet/helloworld時(shí),服務(wù)器通過(guò)“/helloworld”找到了綁定的Servlet名稱(chēng)為cn.itcast.servlet.HelloServlet贴谎,然后服務(wù)器查看這個(gè)類(lèi)型的Servlet是否已經(jīng)創(chuàng)建過(guò)汞扎,如果沒(méi)有創(chuàng)建過(guò),那么服務(wù)器才會(huì)通過(guò)反射來(lái)創(chuàng)建HelloServlet的實(shí)例擅这。當(dāng)我們?cè)俅卧L問(wèn)http://localhost:8080/helloservlet/helloworld時(shí)澈魄,服務(wù)器就不會(huì)再次創(chuàng)建HelloServlet實(shí)例了,而是直接使用上次創(chuàng)建的實(shí)例仲翎。
在Servlet被創(chuàng)建后痹扇,服務(wù)器會(huì)馬上調(diào)用Servlet的void init(ServletConfig)方法铛漓。請(qǐng)記住, Servlet出生后馬上就會(huì)調(diào)用init()方法帘营,而且一個(gè)Servlet的一生票渠。這個(gè)方法只會(huì)被調(diào)用一次逐哈。這好比小孩子出生后馬上就要去剪臍帶一樣芬迄,而且剪臍帶一生只有一次。
我們可以把一些對(duì)Servlet的初始化工作放到init方法中昂秃!
Servlet服務(wù)
當(dāng)服務(wù)器每次接收到請(qǐng)求時(shí)禀梳,都會(huì)去調(diào)用Servlet的service()方法來(lái)處理請(qǐng)求。服務(wù)器接收到一次請(qǐng)求肠骆,就會(huì)調(diào)用service() 方法一次算途,所以service()方法是會(huì)被調(diào)用多次的。正因?yàn)槿绱耸赐龋晕覀儾判枰烟幚碚?qǐng)求的代碼寫(xiě)到service()方法中嘴瓤!
Servlet的離去
Servlet是不會(huì)輕易離去的,通常都是在服務(wù)器關(guān)閉時(shí)Servlet才會(huì)離去莉钙!在服務(wù)器被關(guān)閉時(shí)廓脆,服務(wù)器會(huì)去銷(xiāo)毀Servlet,在銷(xiāo)毀Servlet之前服務(wù)器會(huì)先去調(diào)用Servlet的destroy()方法磁玉,我們可以把Servlet的臨終遺言放到destroy()方法中停忿,例如對(duì)某些資源的釋放等代碼放到destroy()方法中。
測(cè)試生命周期方法
修改helloservlet的代碼:
public class HelloServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {
System.out.println("Servlet被創(chuàng)建了蚊伞!");
}
public ServletConfig getServletConfig() {return null;}
public void destroy() {
System.out.println("Servlet要離去了席赂!");
}
public String getServletInfo() {return null;}
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
System.out.println("hello servlet!");
}
}
在首次訪問(wèn)HelloServlet時(shí),init方法會(huì)被執(zhí)行时迫,而且也會(huì)執(zhí)行service方法颅停。再次訪問(wèn)時(shí),只會(huì)執(zhí)行service方法掠拳,不再執(zhí)行init方法癞揉。在關(guān)閉Tomcat時(shí)會(huì)調(diào)用destroy方法。
Servlet接口相關(guān)類(lèi)型
在Servlet接口中還存在三個(gè)我們不熟悉的類(lèi)型:
- ServletRequest:service() 方法的參數(shù)碳想,它表示請(qǐng)求對(duì)象烧董,它封裝了所有與請(qǐng)求相關(guān)的數(shù)據(jù),它是由服務(wù)器創(chuàng)建的胧奔;
- ServletResponse:service()方法的參數(shù)逊移,它表示響應(yīng)對(duì)象,在service()方法中完成對(duì)客戶端的響應(yīng)需要使用這個(gè)對(duì)象龙填;
- ServletConfig:init()方法的參數(shù)胳泉,它表示Servlet配置對(duì)象拐叉,它對(duì)應(yīng)Servlet的配置信息,那對(duì)應(yīng)web.xml文件中的<servlet>元素扇商。
ServletRequest和ServletResponse
ServletRequest和ServletResponse是Servlet#service() 方法的兩個(gè)參數(shù)凤瘦,一個(gè)是請(qǐng)求對(duì)象,一個(gè)是響應(yīng)對(duì)象案铺,可以從ServletRequest對(duì)象中獲取請(qǐng)求數(shù)據(jù)蔬芥,可以使用ServletResponse對(duì)象完成響應(yīng)。你以后會(huì)發(fā)現(xiàn)控汉,這兩個(gè)對(duì)象就像是一對(duì)恩愛(ài)的夫妻笔诵,永遠(yuǎn)不分離,總是成對(duì)出現(xiàn)姑子。
ServletRequest和ServletResponse的實(shí)例由服務(wù)器創(chuàng)建乎婿,然后傳遞給service()方法。
HttpServletRequest方法:
- String getParameter(String paramName):獲取指定請(qǐng)求參數(shù)的值街佑;
- String getMethod():獲取請(qǐng)求方法谢翎,例如GET或POST;
- String getHeader(String name):獲取指定請(qǐng)求頭的值沐旨;
- void setCharacterEncoding(String encoding):設(shè)置請(qǐng)求體的編碼森逮!因?yàn)镚ET請(qǐng)求沒(méi)有請(qǐng)求體,所以這個(gè)方法只只對(duì)POST請(qǐng)求有效希俩。當(dāng)調(diào)用request.setCharacterEncoding(“utf-8”)之后吊宋,再通過(guò)getParameter()方法獲取參數(shù)值時(shí),那么參數(shù)值都已經(jīng)通過(guò)了轉(zhuǎn)碼颜武,即轉(zhuǎn)換成了UTF-8編碼璃搜。所以,這個(gè)方法必須在調(diào)用getParameter()方法之前調(diào)用鳞上!
HttpServletResponse方法:
- PrintWriter getWriter():獲取字符響應(yīng)流这吻,使用該流可以向客戶端輸出響應(yīng)信息。例如response.getWriter().print(“Hello JavaWeb!”)篙议;
- ServletOutputStream getOutputStream():獲取字節(jié)響應(yīng)流唾糯,當(dāng)需要向客戶端響應(yīng)字節(jié)數(shù)據(jù)時(shí),需要使用這個(gè)流鬼贱,例如要向客戶端響應(yīng)圖片移怯;
- void setCharacterEncoding(String encoding):用來(lái)設(shè)置字符響應(yīng)流的編碼,例如在調(diào)用setCharacterEncoding(“utf-8”);之后这难,再response.getWriter()獲取字符響應(yīng)流對(duì)象舟误,這時(shí)的響應(yīng)流的編碼為utf-8,使用response.getWriter()輸出的中文都會(huì)轉(zhuǎn)換成utf-8編碼后發(fā)送給客戶端姻乓;
- void setHeader(String name, String value):向客戶端添加響應(yīng)頭信息嵌溢,例如setHeader(“Refresh”, “3;url=http://www.itcast.cn”)眯牧,表示3秒后自動(dòng)刷新到http://www.itcast.cn;
- void setContentType(String contentType):該方法是setHeader(“content-type”, “xxx”)的簡(jiǎn)便方法赖草,即用來(lái)添加名為content-type響應(yīng)頭的方法学少。content-type響應(yīng)頭用來(lái)設(shè)置響應(yīng)數(shù)據(jù)的MIME類(lèi)型,例如要向客戶端響應(yīng)jpg的圖片秧骑,那么可以setContentType(“image/jepg”)版确,如果響應(yīng)數(shù)據(jù)為文本類(lèi)型,那么還要臺(tái)同時(shí)設(shè)置編碼腿堤,例如setContentType(“text/html;chartset=utf-8”)表示響應(yīng)數(shù)據(jù)類(lèi)型為文本類(lèi)型中的html類(lèi)型阀坏,并且該方法會(huì)調(diào)用setCharacterEncoding(“utf-8”)方法如暖;
- void sendError(int code, String errorMsg):向客戶端發(fā)送狀態(tài)碼笆檀,以及錯(cuò)誤消息。例如給客戶端發(fā)送404:response(404, “您要查找的資源不存在盒至!”)酗洒。
ServletConfig
ServletConfig對(duì)象對(duì)應(yīng)web.xml文件中的<servlet>元素。例如你想獲取當(dāng)前Servlet在web.xml文件中的配置名枷遂,那么可以使用servletConfig.getServletName()方法獲扔V浴!
ServletConfig對(duì)象是由服務(wù)器創(chuàng)建的酒唉,然后傳遞給Servlet的init()方法矩桂,你可以在init()方法中使用它!
- String getServletName():獲取Servlet在web.xml文件中的配置名稱(chēng)痪伦,即<servlet-name>指定的名稱(chēng)侄榴;
- ServletContext getServletContext():用來(lái)獲取ServletContext對(duì)象,ServletContext會(huì)在后面講解网沾;
- String getInitParameter(String name):用來(lái)獲取在web.xml中配置的初始化參數(shù)癞蚕,通過(guò)參數(shù)名來(lái)獲取參數(shù)值;
- Enumeration getInitParameterNames():用來(lái)獲取在web.xml中配置的所有初始化參數(shù)名稱(chēng)辉哥;
在OneServlet中桦山,可以使用ServletConfig對(duì)象的getInitParameter()方法來(lái)獲取初始化參數(shù),例如:
String value1 = servletConfig.getInitParameter(“paramName1”);//獲取到paramValue1
servlet細(xì)節(jié)
servlet與線程安全
因?yàn)橐粋€(gè)類(lèi)型的Servlet只有一個(gè)實(shí)例對(duì)象醋旦,那么就有可能會(huì)現(xiàn)時(shí)出一個(gè)Servlet同時(shí)處理多個(gè)請(qǐng)求恒水,那么Servlet是否為線程安全的呢?答案是:“不是線程安全的”饲齐。這說(shuō)明Servlet的工作效率很高钉凌,但也存在線程安全問(wèn)題!
所以我們不應(yīng)該在Servlet中便宜創(chuàng)建成員變量箩张,因?yàn)榭赡軙?huì)存在一個(gè)線程對(duì)這個(gè)成員變量進(jìn)行寫(xiě)操作甩骏,另一個(gè)線程對(duì)這個(gè)成員變量進(jìn)行讀操作窗市。
ServletContext
一個(gè)項(xiàng)目只有一個(gè)ServletContext對(duì)象!
我們可以在N多個(gè)Servlet中來(lái)獲取這個(gè)唯一的對(duì)象饮笛,使用它可以給多個(gè)Servlet傳遞數(shù)據(jù)咨察!
與天地同壽!8G唷摄狱!這個(gè)對(duì)象在Tomcat啟動(dòng)時(shí)就創(chuàng)建,在Tomcat關(guān)閉時(shí)才會(huì)死去无午!
ServletContext概述
服務(wù)器會(huì)為每個(gè)應(yīng)用創(chuàng)建一個(gè)ServletContext對(duì)象:
- ServletContext對(duì)象的創(chuàng)建是在服務(wù)器啟動(dòng)時(shí)完成的
- ServletContext對(duì)象的銷(xiāo)毀是在服務(wù)器關(guān)閉時(shí)完成的媒役。
ServletContext對(duì)象的作用是在整個(gè)Web應(yīng)用的動(dòng)態(tài)資源之間共享數(shù)據(jù)!例如在AServlet中向ServletContext對(duì)象中保存一個(gè)值宪迟,然后在BServlet中就可以獲取這個(gè)值酣衷,這就是共享數(shù)據(jù)了。
獲取ServletContext
在Servlet中獲取ServletContext對(duì)象:
- 在void init(ServletConfig config)中:ServletContext context = config.getServletContext();次泽,ServletConfig類(lèi)的getServletContext()方法可以用來(lái)獲取ServletContext對(duì)象穿仪;
在GenericeServlet或HttpServlet中獲取ServletContext對(duì)象:
- GenericServlet類(lèi)有g(shù)etServletContext()方法,所以可以直接使用this.getServletContext()來(lái)獲纫饣纭啊片;
public class MyServlet implements Servlet {
public void init(ServletConfig config) {
ServletContext context = config.getServletContext();
}
…
}
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletContext context = this.getServletContext();
}
}
域?qū)ο蟮墓δ?/h3>
域?qū)ο缶褪怯脕?lái)在多個(gè)Servlet中傳遞數(shù)據(jù)!>料瘛紫谷!l 域?qū)ο蟊仨氂幸?strong>存數(shù)據(jù)功能,域?qū)ο蟊仨氁?strong>取數(shù)據(jù)功能,域?qū)ο髢?nèi)部其實(shí)有一個(gè)Map.
ServletContext是JavaWeb四大域?qū)ο笾唬?/p>
PageContext;ServletRequest捐寥;HttpSession笤昨;ServletContext;
所有域?qū)ο蠖加写嫒?shù)據(jù)的功能上真,因?yàn)橛驅(qū)ο髢?nèi)部有一個(gè)Map咬腋,用來(lái)存儲(chǔ)數(shù)據(jù),下面是ServletContext對(duì)象用來(lái)操作數(shù)據(jù)的方法:
- void setAttribute(String name, Object value):用來(lái)存儲(chǔ)一個(gè)對(duì)象睡互,也可以稱(chēng)之為存儲(chǔ)一個(gè)域?qū)傩愿停纾簊ervletContext.setAttribute(“xxx”, “XXX”),在ServletContext中保存了一個(gè)域?qū)傩跃椭椋驅(qū)傩悦Q(chēng)為xxx寇壳,域?qū)傩缘闹禐閄XX。請(qǐng)注意妻怎,如果多次調(diào)用該方法壳炎,并且使用相同的name,那么會(huì)覆蓋上一次的值,這一特性與Map相同匿辩;
- Object getAttribute(String name):用來(lái)獲取ServletContext中的數(shù)據(jù)腰耙,當(dāng)前在獲取之前需要先去存儲(chǔ)才行,例如:String value = (String)servletContext.getAttribute(“xxx”);铲球,獲取名為xxx的域?qū)傩裕?/li>
- void removeAttribute(String name):用來(lái)移除ServletContext中的域?qū)傩酝ε樱绻麉?shù)name指定的域?qū)傩圆淮嬖冢敲幢痉椒ㄊ裁炊疾蛔觯?/li>
- Enumeration getAttributeNames():獲取所有域?qū)傩缘拿Q(chēng)稼病;
獲取資源相關(guān)方法
獲取文件的真實(shí)(服務(wù)器)路徑
方法:String getRealPath(String path)
web目錄下資源訪問(wèn) String b = context.getRealPath("/b.txt");
WEB-INF目錄下的資源訪問(wèn) String c = context.getRealPath("/WEB-INF/c.txt");
src目錄下的資源訪問(wèn) String a = context.getRealPath("/WEB-INF/classes/a.txt");
獲取資源流
不只可以獲取資源的路徑选侨,還可以通過(guò)ServletContext獲取資源流,即把資源以輸入流的方式獲热蛔摺:
- 獲取a.txt資源流:InputStream in = servletContext.getResourceAsStream(“/a.txt”)援制;
- 獲取b.txt資源流:InputStream in = servletContext.getResourceAsStream(“/WEB-INF/b.txt”);
獲取指定目錄下所有資源路徑
還可以使用ServletContext獲取指定目錄下所有資源路徑芍瑞,例如獲取/WEB-INF下所有資源的路徑:
Set set = context.getResourcePaths("/WEB-INF");
System.out.println(set);
[/WEB-INF/lib/, /WEB-INF/classes/, /WEB-INF/b.txt, /WEB-INF/web.xml]
注意晨仑,本方法必須以“/”開(kāi)頭!W那伞寻歧!
Servlet3.0
支持注解配置≈绕停可以不需要web.xml了。
@WebServlet("/loginServlet")