JavaWeb學習筆記(七)--Servlet入門

1. Servlet簡介

Servlet是Sun公司提供的一門用於開發動態web資源的技術。html

Sun公司在其API中提供了一個Servlet接口,用戶若想開發一個動態web資源(即開發一個Java程序向瀏覽器輸出數據),須要完成如下兩個步驟:java

  1. 編寫一個Java類,實現Servlet接口
  2. 把開發好的Java類部署到web服務器上

按照一種也定俗稱的稱呼習慣,一般咱們也把實現了Servlet接口的Java程序,稱之爲Servlet。web

1.1 實現第一個Servlet

經過查看Servlet API文檔,能夠知道全部的請求都由service方法實現,只須要實現了sevice方法就能夠向瀏覽器輸出數據。因爲Servlet是個接口,咱們本身建立類須要實現全部的方法,好在Sun公司已經提供了實現Servlet接口的兩個類GenericServletHttpServlet,咱們只須要繼承這兩個中的其中一個便可。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,驗證結果

2. Servlet調用過程和生命週期

2.1 UML描述調用過程:

2.2 Servlet運行過程:

Servlet程序是由web服務器調用,web服務器收到客戶端的Servlet訪問請求後:

web服務器首先檢查是否已經裝載並建立了該Servlet實例對象。若是是,則執行第4步,不然,執行第二步。

裝載並建立該Servlet的一個實例對象。

調用Servlet實例對象的init()方法。

建立一個用於封裝HTTP請求的HttpServletRequest對象和一個表明HTTP響應的HttpServletResponse對象,而後調用Servlet的service()方法,並將請求和響應對象做爲參數傳遞進去

web應用程序被中止或從新啓動以前,Servlet引擎將卸載Servlet,並在卸載前調用Servlet的destroy()方法

運行圖解:

 

 

3. 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方法。詳細信息可查看Servlet API文檔

4. 使用IDEA開發Servlet

工欲善其事,必先利其器。開發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,驗證結果:

5. 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的對外訪問路徑。例如:

<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>

5.1.1 Servlet映射多個URL

同一個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>

5.1.2 URL使用*通配符

在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>

若是咱們定義了以下一些映射關係:

  • Servlet1 映射到 /abc/*
  • Servlet2 映射到 /*
  • Servlet3 映射到 /abc
  • Servlet4 映射到 *.do

問題:

  • 當請求URL爲「/abc/a.html」,「/abc/*」 和 「/*」 都匹配,哪一個Servlet響應? Servlet1
  • 當請求URL爲「/abc」,「/abc/*」 和 「/abc」 都匹配,哪一個Servlet響應?  Servlet3
  • 當請求URL爲「/abc/a.do」,「/abc/*」 和 「*.do」 都匹配,哪一個Servlet響應? Servlet1
  • 當請求URL爲「/a.do」,「/*」 和 「*.do」 都匹配,哪一個Servlet響應? Servlet2
  • 當請求URL爲「/xxx/yyy/a.do」, 「/*」 和 「*.do」都匹配,哪一個Servlet響應? Servlet2

5.1.3 配置缺省Servlet

若是某個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應用的靜態資源就沒法訪問了)

5.2 Servlet生命週期和調用方式

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方法。

5.3 Servlet線程安全問題

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。

相關文章
相關標籤/搜索