java基礎學習:JavaWeb之Servlet

Hiway發表於2018-12-16

其他更多java基礎文章:
java基礎學習(目錄)


什麼是Servlet

Servlet是一個特殊的Java類, 是執行在 Web 伺服器中的小型 Java 程式(即:伺服器端的小應用程式)。servlet 通常通過 HTTP(超文字傳輸協議)接收和響應來自 Web 客戶端的請求。這個Java類必須繼承HttpServlet。每個Servlet可以響應客戶端的請求,Servlet提供不同的方法用於響應客戶端請求,例如doGet,doPost,doPut等

Tomcat與Servlet的關係

Tomcat 是Web應用伺服器,是一個Servlet/JSP容器. Tomcat 作為Servlet容器,負責處理客戶請求,把請求傳送給Servlet,並將Servlet的響應傳送回給客戶.而Servlet是一種執行在支援Java語言的伺服器上的元件.。

Servlet最常見的用途是擴充套件Java Web伺服器功能,提供非常安全的,可移植的,易於使用的CGI替代品。 從http協議中的請求和響應可以得知,瀏覽器發出的請求是一個請求文字,而瀏覽器接收到的也應該是一個響應文字。

image

  1. Tomcat將http請求文字接收並解析,然後封裝成HttpServletRequest型別的request物件,所有的HTTP頭資料讀可以通過request物件呼叫對應的方法查詢到。
  2. Tomcat同時會要響應的資訊封裝為HttpServletResponse型別的response物件,通過設定response屬性就可以控制要輸出到瀏覽器的內容,然後將response交給tomcat,tomcat就會將其變成響應文字的格式傳送給瀏覽器。

Java Servlet API 是Servlet容器(tomcat)和servlet之間的介面,它定義了serlvet的各種方法,還定義了Servlet容器傳送給Servlet的物件類,其中最重要的就是ServletRequest和ServletResponse。所以說我們在編寫servlet時,需要實現Servlet介面,按照其規範進行操作。

Servlet執行過程

  在瀏覽器的位址列輸入:http://ip:port/appNames/servlet

  1)通過瀏覽器和ip:port和這個伺服器建立連線。
  2) 瀏覽器會生成一個請求資料包(路徑appNames/servlet)向伺服器傳送請求。
  3) 伺服器收到請求資料包,分析請求資源路徑做精準定位,通過請求的appName查詢webapps檔案下面的appName做匹配,匹配上了需要獲取web.xml中的servlet(mapping)。 
  4) 伺服器建立兩個物件:
    第一個物件:請求物件,該物件實現了HttpServletRequest介面,伺服器會將請求資料包中的資料解析出來,儲存在該物件裡。這樣做的好處是沒有必要理解http協議,只需要讀取request。
    第二個物件:響應物件,實現了HttpServletResponse介面,作用是servlet處理完成後的結果可以存放到該物件上,然後伺服器依據該物件的資料生成響應資料包。
  5) servlet在執行servlet()方法時,可以通過request獲取請求資料,也可以將處理結果存放到response上。然後伺服器與響應物件直接形成一個默契,生成一個響應資料包給瀏覽器。
  6)瀏覽器解析伺服器返回的響應資料包,生成響應的結果。

  

image

Servlet訪問的過程:
Http請求---->web.xml--------> url -pattern----->servlet-name----->servlet-class-----> QuickStratServlet(對應的Class檔案)

Servlet的生命週期

Servlet生命週期可分為5個步驟

  1. 載入Servlet。當Tomcat第一次訪問Servlet的時候,Tomcat會負責建立Servlet的例項
  2. 初始化。當Servlet被例項化後,Tomcat會呼叫init()方法初始化這個物件
  3. 處理服務。當瀏覽器訪問Servlet的時候,Servlet 會呼叫service()方法處理請求
  4. 銷燬。當Tomcat關閉時或者檢測到Servlet要從Tomcat刪除的時候會自動呼叫destroy()方法,讓該例項釋放掉所佔的資源。一個Servlet如果長時間不被使用的話,也會被Tomcat自動銷燬
  5. 解除安裝。當Servlet呼叫完destroy()方法後,等待垃圾回收。如果有需要再次使用這個Servlet,會重新呼叫init()方法進行初始化操作。

簡單總結:只要訪問Servlet,service()就會被呼叫。init()只有第一次訪問Servlet的時候才會被呼叫。 destroy()只有在Tomcat關閉的時候才會被呼叫。

Servlet的配置

使用配置檔案
  1. 寫Servlet類
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
    }
}
複製程式碼
  1. 在web.xml中配置Servlet
    image.png
    ######使用springboot
  2. 寫servlet類
  3. 在springboot啟動類配置
@SpringBootApplication
public class ServletApp {

    @Bean
    public ServletRegistrationBean MyServlet(){
        return new ServletRegistrationBean(new MyServlet(),"/myserv/*");
    }

    public static void main(String[] args){
        SpringApplication.run(ServletApp.class, args);
    }
}
複製程式碼

#Servlet細節

  • Servlet的url匹配順序

當一個請求傳送到servlet容器的時候,容器先會將請求的url減去當前應用上下文的路徑作為servlet的對映url,比如我訪問的是http://localhost/hiway/user/aaa.html,我的應用上下文是hiway,容器會將http://localhost/hiway去掉,剩下的/user/aaa.html部分拿來做servlet的對映匹配。這個對映匹配過程是有順序的,而且當有一個servlet匹配成功以後,就不會去理會剩下的servlet了。其匹配規則和順序如下:

  1. 精確路徑匹配。例子:比如servletA 的url-pattern為 /test,servletB的url-pattern為 /* ,這個時候,如果我訪問的url為http://localhost/test ,這個時候容器就會先進行精確路徑匹配,發現/test正好被servletA精確匹配,那麼就去呼叫servletA,也不會去理會其他的servlet了。
  2. 最長路徑匹配。例子:servletA的url-pattern為/test/,而servletB的url-pattern為/test/a/,此時訪問http://localhost/test/a時,容器會選擇路徑最長的servlet來匹配,也就是這裡的servletB。
  3. 擴充套件匹配。 如果url最後一段包含擴充套件,容器將會根據擴充套件選擇合適的servlet。例子:servletA的url-pattern:*.action
  4. 最後, 如果前面三條規則都沒有找到一個servlet,容器會根據url選擇對應的請求資源。如果應用定義了一個default servlet,則容器會將請求丟給default servlet
  • Servlet是單例的嗎

在Servlet規範中,對於Servlet單例與多例定義如下:

“Deployment Descriptor”, controls how the servlet container provides instances of the servlet.For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.

上面規範提到: 如果一個Servlet沒有被部署在分散式的環境中,一般web.xml中宣告的一個Servlet只對應一個例項。 而如果一個Servlet實現了SingleThreadModel介面,就會被初始化多個例項。預設20個

所以個人理解Servlet不算單例,只是容器讓它只例項化一次,變現出來的是單例的效果而已

  • #####如何開發執行緒安全的Servlet
  1. 實現 SingleThreadModel 介面
  2. 使用synchronized同步對共享資料的操作
  3. 避免使用例項變數

對上面的三種方法進行測試,可以表明用它們都能設計出執行緒安全的Servlet程式。但是,如果一個Servlet實現了SingleThreadModel介面,Servlet引擎將為每個新的請求建立一個單獨的Servlet例項,這將引起大量的系統開銷。SingleThreadModel在Servlet2.4中已不再提倡使用;同樣如果在程式中使用同步來保護要使用的共享的資料,也會使系統的效能大大下降。這是因為被同步的程式碼塊在同一時刻只能有一個執行緒執行它,使得其同時處理客戶請求的吞吐量降低,而且很多客戶處於阻塞狀態。另外為保證主存內容和執行緒的工作記憶體中的資料的一致性,要頻繁地重新整理快取,這也會大大地影響系統的效能。所以在實際的開發中也應避免或最小化 Servlet 中的同步程式碼;**在Serlet中避免使用例項變數是保證Servlet執行緒安全的最佳選擇。**從Java 記憶體模型也可以知道,方法中的臨時變數是在棧上分配空間,而且每個執行緒都有自己私有的棧空間,所以它們不會影響執行緒的安全

學習資料:lixh1986.iteye.com/blog/235569…

  • Servlet的<load-on-startup>

在servlet的配置當中,

<load-on-startup>1</load-on-startup>
複製程式碼

的含義是:
標記容器是否在啟動的時候就載入這個servlet。當值為0或者大於0時,表示容器在應用啟動時就載入這個servlet;當是一個負數時或者沒有指定時,則指示容器在該servlet被選擇時才載入。正數的值越小,啟動該servlet的優先順序越高。
配置load-on-startup後,servlet在startup後立即載入,但只是呼叫servlet的init()方法,用以初始化該servlet相關的資源。初始化成功後,該servlet可響應web請求;如未配置load-on-startup,容器一般在第一次響應web請求時,會先檢測該servlet是否初始化,如未初始化,則呼叫servlet的init()先初始化,初始化成功後,再響應請求。

  • 預設default Servlet

可以將url-pattern 配置一個/,代表該servlet是預設的servlet。什麼是預設的servlet? 當你訪問資源地址所有的servlet都不匹配時,預設的servlet賦值處理。其實,web應用中所有的資源的響應都是servlet負責,包括靜態資源(html頁面)。(有配置預設的servlet,無法訪問到靜態資源。)

學習資料:www.cnblogs.com/zhangyinhua…

相關文章