初窥Servlet
1. Servlet簡(jiǎn)介
??????? Servlet是sun公司提供的一門用于開發(fā)動(dòng)態(tài)web資源的技術(shù)。sun公司在其API中提供了一個(gè)servlet接口,用戶若想要發(fā)一個(gè)動(dòng)態(tài)web資源,只需要完成以下兩步即可:
?????????? 1)編寫一個(gè)servlet,即實(shí)現(xiàn)servlet接口的Java類;
?????????? 2) 把開發(fā)好的servlet部署到web服務(wù)器中。
??????? Servlet接口定義了所有servlet必須實(shí)現(xiàn)的方法。一個(gè)servlet是運(yùn)行在web服務(wù)器中的一個(gè)小java程序,servlet通過HTTP協(xié)議接收并響應(yīng)web客戶端發(fā)來的請(qǐng)求。這個(gè)接口中定義的方法包括初始化servlet,服務(wù)請(qǐng)求,以及remove servlet,這些方法即servlet的生命周期方法,調(diào)用順序如下:
?????????? 1)某個(gè)創(chuàng)建servlet對(duì)象的時(shí)候,調(diào)用初始化方法: void init(ServletConfig config)
?????? ??? 2)客戶端發(fā)送請(qǐng)求的時(shí)候,service方法被執(zhí)行:void service(ServletRequest req, ServletResponse res)
?????? ? ? 3)某個(gè)servlet對(duì)象被摧毀的時(shí)候,調(diào)用destroy方法: void destroy()
??????? 除了這些與生命周期有關(guān)的方法外,Servlet接口還提供了getServletConfig方法和getServletInfo方法,getServletConfig方法可以獲得servlet的一些啟動(dòng)信息,getServletInfo方法可以返回servlet本身的一些信息。
??????? sun公司定義了Servlet接口的兩個(gè)默認(rèn)實(shí)現(xiàn)類,分別為javax.servlet.GenericServlet和javax.servlet.http.HttpServlet。
??????? HttpServlet指能夠處理HTTP請(qǐng)求的servlet,它再原有的Servlet接口上添加了一些與HTTP協(xié)議處理方法,它比Servlet接口的功能更為強(qiáng)大,因此開發(fā)人員在編寫Servlet時(shí),通常應(yīng)繼承這個(gè)類,而避免直接去實(shí)現(xiàn)Servlet接口。HttpServlet在實(shí)現(xiàn)Servlet接口時(shí),覆寫了service方法,該方法體內(nèi)的代碼會(huì)自動(dòng)判斷用戶的請(qǐng)求方式,如為GET請(qǐng)求,則調(diào)用HttpServlet的doGet方法,如為Post請(qǐng)求,則調(diào)用doPost方法。因此,開發(fā)人員在編寫Servlet時(shí),通常只需要覆寫doGet或doPost方法,而不要去覆寫service方法。
2. Servlet的運(yùn)行過程
??????? Servlet程序是由web服務(wù)器調(diào)用,web服務(wù)器接收到客戶端的Servlet訪問請(qǐng)求后:
?????????? 1)web服務(wù)器首先檢查是否已經(jīng)裝載并創(chuàng)建了該Servlet的實(shí)例對(duì)象,如果是,則直接執(zhí)行第4步,否則執(zhí)行第2步。
? ? ? ? ?? 2)裝載并創(chuàng)建該Servlet的一個(gè)實(shí)例對(duì)象。
? ? ? ? ?? 3)調(diào)用Servlet實(shí)例對(duì)象的init()方法。
? ? ? ? ?? 4)創(chuàng)建一個(gè)用于封裝HTTP請(qǐng)求消息的HttpServletRequest對(duì)象和一個(gè)代表HTTP相應(yīng)消息的HttpServletResponse對(duì)象,然后調(diào)用Servlet的service()方法并將請(qǐng)求和響應(yīng)對(duì)象作為參數(shù)傳遞進(jìn)去。
? ? ? ? ?? 5)web應(yīng)用程序被停止或重新啟動(dòng)前,Servlet引擎將卸載Servlet,并在卸載之前調(diào)用Servlet的destroy()方法。
3. Servlet的幾個(gè)細(xì)節(jié)
1. Servlet細(xì)節(jié)1
??????? 由于客戶端是通過URL地址訪問文本服務(wù)器中的資源,所以servlet程序若想被外界訪問,必須把servlet程序映射到一個(gè)URL地址上,這個(gè)工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
?????? 1) <servlet>元素用于注冊(cè)Servlet,它包含兩個(gè)主要的子元素:
????????????? <servlet-name>:用于設(shè)置Servlet的注冊(cè)名稱
????????????? <servlet-class>:用于設(shè)置Servlet的完整類名
??????? 2)<servlet-mapping>元素用于映射一個(gè)已注冊(cè)的Servlet的一個(gè)對(duì)外訪問路徑,它包含兩個(gè)子元素:
????????????? <servlet-name>:用于設(shè)置Servlet的注冊(cè)名稱
????????????? <url-pattern>:用于指定Servlet的對(duì)外訪問路徑(可以隨便指定)
????????例如:
<web-app> <servlet> <servlet-name>AnyName</servlet-name> <servlet-class>HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AnyName</servlet-name> <url-pattern>/demo/hello.html</url-pattern> </servlet-mapping> </web-app>2. Servlet細(xì)節(jié)2
??????? 同一個(gè)Servlet可以被映射到多個(gè)URL上,即多個(gè)<servlet-mapping>元素的<servlet-name>子元素的設(shè)置值可以是同一個(gè)Servlet的注冊(cè)名。在Servlet映射到的URL中也可以使用*通配符,但是只能有兩種固定的格式:一種格式是:“*.擴(kuò)展名”;另一種格式是:以“/”開頭,并以“/*”結(jié)尾。例如:
<servlet-mapping> <servlet-name>AnyName</servlet-name> <url-pattern>*do</url-pattern> </servlet-mapping>?也可以映射到另一個(gè)url:
<servlet-mapping> <servlet-name>AnyName</servlet-name> <url-pattern>/action/*</url-pattern> </servlet-mapping>3. servlet細(xì)節(jié)3
??????? Servlet是一個(gè)供其他java程序(Servlet引擎)調(diào)用的java類,它不能獨(dú)立運(yùn)行,它的運(yùn)行完全由Servlet引擎來控制和調(diào)度。針對(duì)客戶端的多次Servlet請(qǐng)求,通常情況下,服務(wù)器只會(huì)創(chuàng)建一個(gè)Servlet實(shí)例對(duì)象,也就是說Servlet實(shí)例對(duì)象一旦創(chuàng)建,它就會(huì)駐留在內(nèi)存中,為后續(xù)的其它請(qǐng)求服務(wù),直至web容器退出,servlet實(shí)例對(duì)象才銷毀。
??????? 在Servlet的整個(gè)生命周期中,Servlet的init方法只被調(diào)用一次,而對(duì)一個(gè)Servlet的每次訪問請(qǐng)求都導(dǎo)致Servlet引擎調(diào)用一次servlet的service方法。對(duì)于每次訪問請(qǐng)求,Servlet引擎都會(huì)創(chuàng)建一個(gè)新的HttpServletRequest請(qǐng)求對(duì)象和一個(gè)新的HttpServletResponse響應(yīng)對(duì)象,然后將這兩個(gè)對(duì)象作為參數(shù)傳遞給它調(diào)用的Servlet的service()方法,service方法再根據(jù)請(qǐng)求方式分別調(diào)用doGet或doPost方法。
4. servlet細(xì)節(jié)4
??????? 如果再<servlet>元素中配置了一個(gè)<load-on-startup>元素,那么web應(yīng)用程序在啟動(dòng)時(shí),就會(huì)裝載并創(chuàng)建Servlet的實(shí)例對(duì)象,以及調(diào)用Servlet實(shí)例對(duì)象的init()方法。該技術(shù)可以用來為web寫一個(gè)initServlet,這個(gè)servlet配置為啟動(dòng)時(shí)裝載,為整個(gè)web應(yīng)用床架必要的數(shù)據(jù)庫表和數(shù)據(jù)。如:
<servlet> <servlet-name>...</servlet-name> <servlet-class>...</servlet-class> <load-on-startup>1<load-on-startiup> </servlet>5. servlet細(xì)節(jié)5
??????? 如果某個(gè)Servlet的映射路徑僅僅為一個(gè)正斜杠(/),那么這個(gè)Servlet就成為當(dāng)前Web應(yīng)用程序的缺省Servlet。凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的url,它們的訪問請(qǐng)求都將交給缺省Servlet處理,也就是說,缺省Servlet用于處理所有其他Servlet都不處理的訪問請(qǐng)求。
??????? 在<tomcat安裝目錄>\conf\web.xml文件中,注冊(cè)了一個(gè)名稱為org.apache.catalina.servlets.DefaulServlet的Servlet,并將這個(gè)Servlet設(shè)置為了缺省Servlet。當(dāng)訪問Tomcat服務(wù)器中的某個(gè)靜態(tài)HTML文件和圖片時(shí),實(shí)際上是在訪問這個(gè)缺省Servlet。
??????? 假設(shè)現(xiàn)在在工程名為test的WebRoot目錄下新建一個(gè)1.html,在web.xml文件中并沒有配置<servlet-mapping>,即在工程中沒有Servlet映射成1.html,然后訪問:http://localhost:8080/test/1.html時(shí),這時(shí)候這個(gè)請(qǐng)求就交給缺省的Servlet,缺省的Servlet收到請(qǐng)求后,會(huì)首先看一下web應(yīng)用下面有沒有這個(gè)1.html,如果有則讀取并返回到瀏覽器,如果沒有則返回一個(gè)錯(cuò)誤頁面。
6. servlet細(xì)節(jié)6
??????? 當(dāng)多個(gè)客戶端并發(fā)訪問同一個(gè)Servlet時(shí),web服務(wù)器會(huì)為每一個(gè)客戶端的訪問請(qǐng)求創(chuàng)建一個(gè)線程,并在這個(gè)線程上調(diào)用Servlet的service方法,因?yàn)镾ervlet只有一個(gè)實(shí)例化對(duì)象,因此service方法內(nèi)如果訪問了同一個(gè)資源的話,就有可能引發(fā)線程安全問題。那么如何解決Servlet中的線程安全問題呢?
?????? 1) 如果某個(gè)Servlet實(shí)現(xiàn)了SingleThreadModel接口,那么Servlet引擎將以單線程模式來調(diào)用其service方法。該接口沒有任何方法,它起到了一個(gè)標(biāo)志的作用。對(duì)于實(shí)現(xiàn)了SingleThreadModel接口的Servlet,Servlet引擎仍然支持對(duì)該Servlet的多線成并發(fā)訪問,其采用的方式是產(chǎn)生多個(gè)Servlet實(shí)例對(duì)象,并發(fā)的每個(gè)線程分別調(diào)用一個(gè)獨(dú)立Servlet實(shí)例對(duì)象。
?????? 2)使用synchronized 關(guān)鍵字,synchronized能保證一次只有一個(gè)線程可以訪問被保護(hù)的區(qū)段,從而可以通過同步塊操作來保證線程的安全。
?????? 3)盡量避免在Servlet里使用實(shí)例變量,只要在Servlet里面的任何方法里面都不使用共有的實(shí)例變量,那么該Servlet就是線程安全的。
? ? ???以上三種方式中,實(shí)現(xiàn)SingleThreadModel接口可以解決問題,但是并不是真正解決Servlet的線程安全問題,因?yàn)镾ervlet引擎會(huì)創(chuàng)建多個(gè)Servlet實(shí)例對(duì)象,而真正意義上解決多線程安全問題是指一個(gè)Servlet實(shí)例對(duì)象被多個(gè)線程同時(shí)調(diào)用的問題。另外,創(chuàng)建多個(gè)Servlet實(shí)例對(duì)象也會(huì)引起大量的開銷。事實(shí)上,在Servlet API 2.4中,已經(jīng)將SingleThreadModel標(biāo)記為過時(shí)的了。
????????同樣,如果在程序中使用同步來保護(hù)要使用的共享的數(shù)據(jù),也會(huì)使系統(tǒng)的性能大大下降。這是因?yàn)楸煌降拇a塊在同一時(shí)刻只能有一個(gè)線程執(zhí)行它,使得其同時(shí)處理客戶請(qǐng)求的吞吐量降低,而且很多客戶處于阻塞狀態(tài)。所以在實(shí)際的開發(fā)中也應(yīng)避免或最小化 Servlet 中的同步代碼。
????????所以,在Servlet中避免使用實(shí)例變量是保證Servlet線程安全的最佳選擇。
3. Servlet的Hello Word
??????? 最后寫一個(gè)再簡(jiǎn)單不過的Hello Word吧……
public class ServletDemo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Hello word!"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }?web.xml:
<servlet> <servlet-name>ServletDemo</servlet-name> <servlet-class>servlet.ServletDemo</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/ServletDemo</url-pattern> </servlet-mapping>入門就這么多吧,如有錯(cuò)誤之處,歡迎留言指正~
總結(jié)
- 上一篇: 实现仿简书选取内容生成分享图片效果
- 下一篇: vim cscope taglist 使