servlet到底是什么?對于這個問題一直云里霧里的,今天打算刨根問底。
一、Servlet簡介
Servlet是sun公司提供的一門用于開發動態web資源的技術。
Sun公司在其API中提供了一個servlet接口,用戶若想用發一個動態web資源(即開發一個Java程序向瀏覽器輸出數據),需要完成以下2個步驟:
1、編寫一個Java類,實現servlet接口。
2、把開發好的Java類部署到web服務器中。
按照一種約定俗成的稱呼習慣,通常我們也把實現了servlet接口的java程序,稱之為Servlet
二、Servlet的運行過程
Servlet程序是由WEB服務器調用,web服務器收到客戶端的Servlet訪問請求后:
①Web服務器首先檢查是否已經裝載并創建了該Servlet的實例對象。如果是,則直接執行第④步,否則,執行第②步。
②裝載并創建該Servlet的一個實例對象。?
③調用Servlet實例對象的init()方法。
④創建一個用于封裝HTTP請求消息的HttpServletRequest對象和一個代表HTTP響應消息的HttpServletResponse對象,然后調用Servlet的service()方法并將請求和響應對象作為參數傳遞進去。
⑤WEB應用程序被停止或重新啟動之前,Servlet引擎將卸載Servlet,并在卸載之前調用Servlet的destroy()方法。?
三、Servlet調用圖
四、在Eclipse中開發Servlet
在eclipse中新建一個web project工程,eclipse會自動創建下圖所示目錄結構:
4.1、Servlet接口實現類
Servlet接口SUN公司定義了兩個默認實現類,分別為:GenericServlet、HttpServlet。
HttpServlet指能夠處理HTTP請求的servlet,它在原有Servlet接口上添加了一些與HTTP協議處理方法,它比Servlet接口的功能更為強大。因此開發人員在編寫Servlet時,通常應繼承這個類,而避免直接去實現Servlet接口。
HttpServlet在實現Servlet接口時,覆寫了service方法,該方法體內的代碼會自動判斷用戶的請求方式,如為GET請求,則調用HttpServlet的doGet方法,如為Post請求,則調用doPost方法。因此,開發人員在編寫Servlet時,通常只需要覆寫doGet或doPost方法,而不要去覆寫service方法。
4.2、通過Eclipse創建和編寫Servlet
選中com.hhl.servlet包,右鍵→New→Servlet,如下圖所示:
這樣,我們就通過Eclipse幫我們創建好一個名字為HelloServlet的Servlet,創建好的HelloServlet里面會有如下代碼:
[java]?view plaincopy
public?class?HelloServlet?extends?HttpServlet?{????????????????private?static?final?long?serialVersionUID?=?-468861535655735406L;????????????????public?HelloServlet()?{??????????super();??????}????????????????public?void?destroy()?{??????????super.destroy();?????????????????}???????????????????????public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)??????????????throws?ServletException,?IOException?{??????????response.setContentType("text/html");??????????????PrintWriter?out?=?response.getWriter();??????????out.println("<!DOCTYPE?HTML?PUBLIC?\"-//W3C//DTD?HTML?4.01?Transitional//EN\">");??????????out.println("<HTML>");??????????out.println("??<HEAD><TITLE>A?Servlet</TITLE></HEAD>");??????????out.println("??<BODY>");??????????????out.print("????This?is?");??????????????out.print(this.getClass());??????????????out.println(",?using?the?GET?method");??????????out.println("??</BODY>");??????????out.println("</HTML>");??????????????out.flush();??????????????out.close();????????????????}???????????????????????public?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)??????????????throws?ServletException,?IOException?{????????????response.setContentType("text/html");??????????PrintWriter?out?=?response.getWriter();??????????out.println("<!DOCTYPE?HTML?PUBLIC?\"-//W3C//DTD?HTML?4.01?Transitional//EN\">");??????????out.println("<HTML>");??????????out.println("??<HEAD><TITLE>A?Servlet</TITLE></HEAD>");??????????out.println("??<BODY>");??????????out.print("????This?is?");??????????out.print(this.getClass());??????????out.println(",?using?the?POST?method");??????????out.println("??</BODY>");??????????out.println("</HTML>");??????????out.flush();??????????out.close();??????}??????????????????public?void?init()?throws?ServletException?{??????????System.out.println("servlet?init");??????}??????}??
這些代碼都是Eclipse自動生成的,而web.xml文件中也多了<servlet></servlet>和<servlet-mapping></servlet-mapping>兩對標簽,這兩對標簽是配置HelloServlet的,如下所示:
[java]?view plaincopy
<servlet>??????<description>This?is?the?description?of?my?J2EE?component</description>??????<display-name>This?is?the?display?name?of?my?J2EE?component</display-name>??????<servlet-name>HelloServlet</servlet-name>??????<servlet-class>com.hhl.servlet.HelloServlet</servlet-class>????</servlet>??????<servlet-mapping>??????<servlet-name>HelloServlet</servlet-name>??????<url-pattern>/</url-pattern>????</servlet-mapping>??
然后我們就可以通過瀏覽器訪問HelloServlet這個Servlet,如下圖所示:
五、Servlet開發注意細節
5.1、Servlet訪問URL映射配置
由于客戶端是通過URL地址訪問web服務器中的資源,所以Servlet程序若想被外界訪問,必須把servlet程序映射到一個URL地址上,這個工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
<servlet>元素用于注冊Servlet,它包含有兩個主要的子元素:<servlet-name>和<servlet-class>,分別用于設置Servlet的注冊名稱和Servlet的完整類名。
一個<servlet-mapping>元素用于映射一個已注冊的Servlet的一個對外訪問路徑,它包含有兩個子元素:<servlet-name>和<url-pattern>,分別用于指定Servlet的注冊名稱和Servlet的對外訪問路徑。例如:
[html]?view plaincopy
<servlet>??????<description>This?is?the?description?of?my?J2EE?component</description>??????<display-name>This?is?the?display?name?of?my?J2EE?component</display-name>??????<servlet-name>HelloServlet</servlet-name>??????<servlet-class>com.hhl.servlet.HelloServlet</servlet-class>????</servlet>??????<servlet-mapping>??????<servlet-name>HelloServlet</servlet-name>??????<url-pattern>/</url-pattern>????</servlet-mapping>??
同一個Servlet可以被映射到多個URL上,即多個<servlet-mapping>元素的<servlet-name>子元素的設置值可以是同一個Servlet的注冊名。 例如:
[html]?view plaincopy
<servlet-mapping>??????<servlet-name>HelloServlet</servlet-name>??????<url-pattern>/</url-pattern>????</servlet-mapping>????<servlet-mapping>??????<servlet-name>HelloServlet</servlet-name>??????<url-pattern>/1.html</url-pattern>????</servlet-mapping>??
通過上面的配置,當我們想訪問名稱是HelloServlet的Servlet,可以使用如下的幾個地址去訪問:
http://localhost:8088/ServletDemo/
http://localhost:8088/ServletDemo/1.html
HelloServlet被映射到了多個URL上。
5.2、Servlet訪問URL使用*通配符映射
在Servlet映射到的URL中也可以使用*通配符,但是只能有兩種固定的格式:一種格式是"*.擴展名",另一種格式是以正斜杠(/)開頭并以"/*"結尾。例如:
[html]?view plaincopy
<servlet-mapping>??????<servlet-name>HelloServlet</servlet-name>??????<url-pattern>/*</url-pattern>????</servlet-mapping>??
*可以匹配任意的字符,所以此時可以用任意的URL去訪問ServletDemo1這個Servlet
對于如下的一些映射關系:
Servlet1 映射到 /abc/*?
Servlet2 映射到 /*?
Servlet3 映射到 /abc?
Servlet4 映射到 *.do?
問題:
當請求URL為“/abc/a.html”,“/abc/*”和“/*”都匹配,哪個servlet響應
?? ? Servlet引擎將調用Servlet1。
當請求URL為“/abc”時,“/abc/*”和“/abc”都匹配,哪個servlet響應
?? ? Servlet引擎將調用Servlet3。
當請求URL為“/abc/a.do”時,“/abc/*”和“*.do”都匹配,哪個servlet響應
?? ? Servlet引擎將調用Servlet1。
當請求URL為“/a.do”時,“/*”和“*.do”都匹配,哪個servlet響應
?? ? Servlet引擎將調用Servlet2。
當請求URL為“/xxx/yyy/a.do”時,“/*”和“*.do”都匹配,哪個servlet響應
?? ? Servlet引擎將調用Servlet2。
匹配的原則就是"誰長得更像就找誰"
5.3、Servlet與普通Java類的區別
Servlet是一個供其他Java程序(Servlet引擎)調用的Java類,它不能獨立運行,它的運行完全由Servlet引擎來控制和調度。
針對客戶端的多次Servlet請求,通常情況下,服務器只會創建一個Servlet實例對象,也就是說Servlet實例對象一旦創建,它就會駐留在內存中,為后續的其它請求服務,直至web容器退出,servlet實例對象才會銷毀。(注)
在Servlet的整個生命周期內,Servlet的init方法只被調用一次。而對一個Servlet的每次訪問請求都導致Servlet引擎調用一次servlet的service方法。對于每次訪問請求,Servlet引擎都會創建一個新的HttpServletRequest請求對象和一個新的HttpServletResponse響應對象,然后將這兩個對象作為參數傳遞給它調用的Servlet的service()方法,service方法再根據請求方式分別調用doXXX方法。
如果在<servlet>元素中配置了一個<load-on-startup>元素,那么WEB應用程序在啟動時,就會裝載并創建Servlet的實例對象、以及調用Servlet實例對象的init()方法。
?? ?舉例:
????
[html]?view plaincopy
<servlet>??????<description>This?is?the?description?of?my?J2EE?component</description>??????<display-name>This?is?the?display?name?of?my?J2EE?component</display-name>??????<servlet-name>HelloServlet</servlet-name>??????<servlet-class>com.hhl.servlet.HelloServlet</servlet-class>??????<load-on-startup>1</load-on-startup>????</servlet>??
用途:為web應用寫一個HelloServlet,這個servlet配置為啟動時裝載,為整個web應用創建必要的數據庫表和數據。
5.4、缺省Servlet
如果某個Servlet的映射路徑僅僅為一個正斜杠(/),那么這個Servlet就成為當前Web應用程序的缺省Servlet。
凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它們的訪問請求都將交給缺省Servlet處理,也就是說,缺省Servlet用于處理所有其他Servlet都不處理的訪問請求。 例如:
[html]?view plaincopy
<servlet>?????<description>This?is?the?description?of?my?J2EE?component</description>?????<display-name>This?is?the?display?name?of?my?J2EE?component</display-name>?????<servlet-name>HelloServlet</servlet-name>?????<servlet-class>com.hhl.servlet.HelloServlet</servlet-class>?????<load-on-startup>1</load-on-startup>???</servlet>?????<servlet-mapping>?????<servlet-name>HelloServlet</servlet-name>?????<url-pattern>/</url-pattern>???</servlet-mapping>??
當訪問不存在的Servlet時,就使用配置的默認Servlet進行處理
在<tomcat的安裝目錄>\conf\web.xml文件中,注冊了一個名稱為org.apache.catalina.servlets.DefaultServlet的Servlet,并將這個Servlet設置為了缺省Servlet。
[html]?view plaincopy
<servlet>??????????<servlet-name>default</servlet-name>??????????<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>??????????<init-param>??????????????<param-name>debug</param-name>??????????????<param-value>0</param-value>??????????</init-param>??????????<init-param>??????????????<param-name>listings</param-name>??????????????<param-value>false</param-value>??????????</init-param>??????????<load-on-startup>1</load-on-startup>??????</servlet>????????<servlet-mapping>??????????<servlet-name>default</servlet-name>??????????<url-pattern>/</url-pattern>??????</servlet-mapping>??
當訪問Tomcat服務器中的某個靜態HTML文件和圖片時,實際上是在訪問這個缺省Servlet。
5.5、Servlet的線程安全問題
當多個客戶端并發訪問同一個Servlet時,web服務器會為每一個客戶端的訪問請求創建一個線程,并在這個線程上調用Servlet的service方法,因此service方法內如果訪問了同一個資源的話,就有可能引發線程安全問題。例如下面的代碼:
不存在線程安全問題的代碼:
[java]?view plaincopy
import?java.io.IOException;??????import?javax.servlet.ServletException;???import?javax.servlet.http.HttpServlet;???import?javax.servlet.http.HttpServletRequest;???import?javax.servlet.http.HttpServletResponse;??????public?class?ServletDemo3?extends?HttpServlet?{?????????????????public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)???????????????throws?ServletException,?IOException?{????????????????????????????????????????int?i=1;???????????i++;???????????response.getWriter().write(i);???????}??????????public?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)???????????????throws?ServletException,?IOException?{???????????doGet(request,?response);???????}??????}??
存在線程安全問題的代碼:
[java]?view plaincopy
import?java.io.IOException;??import?javax.servlet.ServletException;??import?javax.servlet.http.HttpServlet;??import?javax.servlet.http.HttpServletRequest;??import?javax.servlet.http.HttpServletResponse;????public?class?ServletDemo3?extends?HttpServlet?{????????int?i=1;??????public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)??????????????throws?ServletException,?IOException?{????????????????????i++;??????????try?{??????????????Thread.sleep(1000*4);??????????}?catch?(InterruptedException?e)?{??????????????e.printStackTrace();??????????}??????????response.getWriter().write(i+"");??????}????????public?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)??????????????throws?ServletException,?IOException?{??????????doGet(request,?response);??????}????}??
把i定義成全局變量,當多個線程并發訪問變量i時,就會存在線程安全問題了,如下圖所示:同時開啟兩個瀏覽器模擬并發訪問同一個Servlet,本來正常來說,第一個瀏覽器應該看到2,而第二個瀏覽器應該看到3的,結果兩個瀏覽器都看到了3,這就不正常。
線程安全問題只存在多個線程并發操作同一個資源的情況下,所以在編寫Servlet的時候,如果并發訪問某一個資源(變量,集合等),就會存在線程安全問題,那么該如何解決這個問題呢?
先看看下面的代碼:
[java]?view plaincopy
import?java.io.IOException;????import?javax.servlet.ServletException;??import?javax.servlet.http.HttpServlet;??import?javax.servlet.http.HttpServletRequest;??import?javax.servlet.http.HttpServletResponse;??????public?class?ServletDemo3?extends?HttpServlet?{????????int?i=1;??????public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)??????????????throws?ServletException,?IOException?{???????????????????????????synchronized?(this)?{??????????????i++;??????????????try?{??????????????????Thread.sleep(1000*4);??????????????}?catch?(InterruptedException?e)?{??????????????????e.printStackTrace();??????????????}??????????????response.getWriter().write(i+"");??????????}????????????????}????????public?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)??????????????throws?ServletException,?IOException?{??????????doGet(request,?response);??????}????}??
現在這種做法是給Servlet對象加了一把鎖,保證任何時候都只有一個線程在訪問該Servlet對象里面的資源,這樣就不存在線程安全問題了。
這種做法雖然解決了線程安全問題,但是編寫Servlet卻萬萬不能用這種方式處理線程安全問題,假如有9999個人同時訪問這個Servlet,那么這9999個人必須按先后順序排隊輪流訪問。
針對Servlet的線程安全問題,Sun公司是提供有解決方案的:讓Servlet去實現一個SingleThreadModel接口,如果某個Servlet實現了SingleThreadModel接口,那么Servlet引擎將以單線程模式來調用其service方法。
查看Sevlet的API可以看到,SingleThreadModel接口中沒有定義任何方法和常量,在Java中,把沒有定義任何方法和常量的接口稱之為標記接口,經常看到的一個最典型的標記接口就是"Serializable",這個接口也是沒有定義任何方法和常量的,標記接口在Java中有什么用呢?主要作用就是給某個對象打上一個標志,告訴JVM,這個對象可以做什么,比如實現了"Serializable"接口的類的對象就可以被序列化,還有一個"Cloneable"接口,這個也是一個標記接口,在默認情況下,Java中的對象是不允許被克隆的,就像現實生活中的人一樣,不允許克隆,但是只要實現了"Cloneable"接口,那么對象就可以被克隆了。
讓的Servlet實現了SingleThreadModel的接口,只要在Servlet的類的定義中增加實現了SingleThreadModel接口的聲明即可。 ?
對于實現了SingleThreadModel的接口的Servlet中,Servlet的引擎仍然支持對該Servlet的的多線程并發訪問,其采用的方式是產生多個Servlet的實例對象,并發的每個線程分別調用一個獨立的Servlet的實例對象。
實現了SingleThreadModel接口并不能真正解決的Servlet的線程安全問題,因為Servlet的引擎會創建多個Servlet的實例對象,而真正意義上解決多線程安全問題是指一個Servlet實例對象被多線程同時調用的問題。事實上,在Servlet API 2.4中,已經將SingleThreadModel標記為Deprecated(過時的)。 ? ? ? ?? 那么防止線程安全的問題就是避免使用實例變量,采用局部變量的形式。如果應用程序設計無法避免使用實例變量,那么使用同步來保護要使用的實例變量,但為保證系統的最佳性能,應該同步可用性最小的代碼路徑。
Servlet的確已經能夠幫我們完成所有的工作了,但是現在的web應用很少有直接將交互全部頁面都用servlet來實現,而是采用更高效的MVC框架來實現。這些MVC框架基本的原理都是將所有的請求都映射到一個Servlet,然后去實現service方法,這個方法也就是MVC框架的入口。如下圖是strust MVC的,為什么是performLogin,秘密就在PubAction.java中
Spring MVC的
轉載:http://blog.csdn.net/honghailiang888。
總結
以上是生活随笔為你收集整理的servlet到底是什么的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。