Servlet是Sun公司提供的一門用於開發動態web資源的技術。html
Sun公司在其API中提供了一個Servlet接口,用戶若想開發一個動態web資源(即開發一個Java程序向瀏覽器輸出數據),須要完成如下兩個步驟:java
按照一種也定俗稱的稱呼習慣,一般咱們也把實現了Servlet接口的Java程序,稱之爲Servlet。web
經過查看Servlet API文檔,能夠知道全部的請求都由service方法實現,只須要實現了sevice方法就能夠向瀏覽器輸出數據。因爲Servlet是個接口,咱們本身建立類須要實現全部的方法,好在Sun公司已經提供了實現Servlet接口的兩個類GenericServlet, HttpServlet,咱們只須要繼承這兩個中的其中一個便可。apache
void service(ServletRequest req, ServletResponse res) // Called by the servlet container to allow the servlet to respond to a request.
1. 在tomcat中建立一個名爲firstServlet的web應用,在web應用中建立WEB-INF/classes文件夾api
2. 在classes文件夾中建立FirstServlet.java瀏覽器
1 package com.servlet; 2 3 import java.io.*; 4 import javax.servlet.*; 5 6 public class FirstServlet extends GenericServlet { 7 8 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { 9 OutputStream out = res.getOutputStream(); // 獲取輸出流 10 out.write("hello servlet".getBytes()); // 向瀏覽器輸出數據 11 } 12 }
3. 在classpath中增長Servlet API對應的jar包,編譯FirstServlet.javatomcat
直接編譯會報錯:安全
設置classpath,編譯成功:服務器
1 set classpath=%classpath%;E:\Program Files\apache-tomcat-8.5.37\lib\servlet-api.jar 2 javac -d . FirstServlet.java
4. 在WEB-INF中建立web.xml,配置Servlet的對外訪問路徑app
web.xml但是能夠參考tomcat中web示例examples,拷貝頭尾和Servlet配置部分便可,修改Servlet名稱和對外訪問路徑:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 5 http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 6 version="3.1" 7 metadata-complete="true"> 8 9 <servlet> 10 <servlet-name>FirstServlet</servlet-name> 11 <servlet-class>com.servlet.FirstServlet</servlet-class> 12 </servlet> 13 <servlet-mapping> 14 <servlet-name>FirstServlet</servlet-name> 15 <url-pattern>/FirstServlet</url-pattern> 16 </servlet-mapping> 17 18 </web-app>
5. 啓動Tomcat,訪問http://localhost:8080/firstServlet/FirstServlet,驗證結果
Servlet程序是由web服務器調用,web服務器收到客戶端的Servlet訪問請求後:
web服務器首先檢查是否已經裝載並建立了該Servlet實例對象。若是是,則執行第4步,不然,執行第二步。
裝載並建立該Servlet的一個實例對象。
調用Servlet實例對象的init()方法。
建立一個用於封裝HTTP請求的HttpServletRequest對象和一個表明HTTP響應的HttpServletResponse對象,而後調用Servlet的service()方法,並將請求和響應對象做爲參數傳遞進去
web應用程序被中止或從新啓動以前,Servlet引擎將卸載Servlet,並在卸載前調用Servlet的destroy()方法
運行圖解:
Servlet接口Sun公司定義了兩個默認實現類,分別是:GenericServlet, HttpServlet。
HttpServlet指可以處理HTTP請求的Servlet,它在原有Servlet接口上添加了一些與HTTP協議處理的方法,它比Servlet接口的功能更爲強大。所以開發人員在編寫Servlet時,一般應繼承這個類,而避免直接去實現Servlet接口。
HttpServlet在實現Servlet接口時,覆寫了service()方法,該方法體內的代碼會自動判斷用戶的請求方式,如爲GET請求,則調用HttpServlet的doGet方法;如爲POST請求,則調用doPost方法。所以,開發人員在編寫Servlet時,一般只須要覆寫doGet或doPost方法,
而不要去覆寫service方法。詳細信息可查看Servlet API文檔。
工欲善其事,必先利其器。開發web應用時,若是每次都是咱們手動建立web應用目錄,很麻煩,又浪費時間。咱們須要一款IDE去幫助咱們跳過這個步驟,推薦使用IDEA 專業版。
1. 在IDEA中新建一個java web工程(通常選擇Java Enterprise),IDEA會自動建立以下目錄結構:
2. 配置Tomcat
通常狀況下IDEA已經把Tomcat已經配置好了,項目直接就能夠運行。若是沒有配置就須要手動添加:
1).點擊菜單中Run
-> Edit Configurations
2) 修改Tomcat名稱(不改也不要緊,好看而已),增長war包,配置瀏覽器打開路徑,和web應用映射路徑
3) 啓動Tomcat(點擊右上角的啓動圖標--右三角),驗證結果
3. 建立Servlet
1)在src目錄下增長Servlet
2)配置Servlet包路徑,並給Servlet起名
3)補充Servlet代碼
在doGet方法中向瀏覽器輸出"Hello Servlet"
1 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2 response.getOutputStream().write("Hello Servlet!!!".getBytes()); 3 }
4)在web.xml中配置Servlet的對外訪問路徑
1 <!-- 2 映射的順序: 瀏覽器獲取到url,匹配到url-pattern(例如:/bbb),而後根據servlet-name去找咱們的servlet-class 3 因此servlet-name能夠隨便取,只要能映射上便可,可是習慣上仍是和Servlet的類名保持一一致 4 --> 5 <servlet> 6 <servlet-name>xxx</servlet-name> 7 <servlet-class>com.servlet.ServletDemo</servlet-class> 8 </servlet> 9 10 <servlet-mapping> 11 <servlet-name>xxx</servlet-name> 12 <url-pattern>/bbb</url-pattern> 13 </servlet-mapping>
5)打開瀏覽器,訪問 http://localhost:8080/first/bbb,驗證結果:
因爲客戶端是經過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的對外訪問路徑。例如:
<servlet> <servlet-name>AnyName</servlet-name> <servlet-class>com.servlet.ServletDemo</servlet-class> </servlet> <servlet-mapping> <servlet-name>AnyName</servlet-name> <url-pattern>/index.html</url-pattern> </servlet-mapping>
同一個Servlet能夠被映射到多個URL上,即多個<servlet-mapping>元素的<servlet-name>子元素的值能夠是同一個Servlet的註冊名。例如:
經過/ServletDemo、/servlet/helloServlet、/hello.html均可以訪問到ServletDemo。
<servlet> <servlet-name>ServletDemo</servlet-name> <servlet-class>com.servlet.ServletDemo</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/ServletDemo</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/servlet/helloServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/hello.html</url-pattern> </servlet-mapping>
在Servlet映射到的URL中也可使用*通配符,可是隻有兩種固定的格式:一種格式是「*.擴展名」,另外一種格式是以正斜槓(/)開頭並以「/*」結尾。例如:
<servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/action/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
若是咱們定義了以下一些映射關係:
問題:
若是某個Servlet的映射路徑僅爲一個正斜槓(/),那麼這個Servlet就成爲當前web應用程序的缺省Servlet。
凡是在web.xml中找不到匹配的<servlet-mapping>元素的URL,它們的訪問請求都將交給缺省的Servlet處理,也就是說,缺省的Servlet用於處理其餘Servlet都不處理的訪問請求。
在Tomcat安裝目錄conf/web.xml中,註冊了一個名爲org.apache.catalina.servlets.DefaultServlet的Servlet,並將這個設置爲了缺省Servlet。
當訪問Tomcat服務器的某個靜態的HTML文件和圖片時,實際上在訪問這個缺省的Servlet(因此通常狀況下咱們不會本身去寫缺省的Servlet,不然web應用的靜態資源就沒法訪問了)
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()方法。<load-on-startup>元素的值(正整數)表示Servlet加載的順序,值越小優先級越高。例如:
<servlet> <servlet-name>ServletDemo</servlet-name> <servlet-class>com.servlet.ServletDemo</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletDemo</servlet-name> <url-pattern>/ServletDemo</url-pattern> </servlet-mapping> <servlet> <servlet-name>ServletDemo2</servlet-name> <servlet-class>com.servlet.ServletDemo2</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ServletDemo2</servlet-name> <url-pattern>/ServletDemo2</url-pattern> </servlet-mapping> <servlet> <servlet-name>ServletDemo1</servlet-name> <servlet-class>com.servlet.ServletDemo1</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ServletDemo1</servlet-name> <url-pattern>/ServletDemo1</url-pattern> </servlet-mapping>
能夠看到配置<load-on-startup>元素的ServletDemo一、ServletDemo2在服務器啓動的時候就調用了init方法,而且ServletDemo1優先加載,而ServletDemo在瀏覽器訪問其URL映射時,才調用其init方法。
Servlet是單實例對象,對於類實例變量,全部線程共享實例變量。當多個線程對共享資源同時訪問就可能引起線程安全問題。
解決方案:使用局部變量,例如:
1 public class ServletDemo5 extends HttpServlet { 2 private int classVariable = 0; 3 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 4 5 } 6 7 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 8 int localVariable = 0; 9 try { 10 for (int i = 0; i < 1000; i++) { 11 classVariable++; 12 localVariable++; 13 Thread.sleep(50); 14 } 15 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 String outPut = "classVariable: " + classVariable + ", localVariable: " + localVariable; 20 response.getOutputStream().write(outPut.getBytes()); 21 } 22 }
啓動3個瀏覽器分別訪問:
類實例變量classVariable不是指望的值1000,局部變量localVariable是指望的值1000。