其他更多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協議中的請求和響應可以得知,瀏覽器發出的請求是一個請求文字,而瀏覽器接收到的也應該是一個響應文字。
- Tomcat將http請求文字接收並解析,然後封裝成HttpServletRequest型別的request物件,所有的HTTP頭資料讀可以通過request物件呼叫對應的方法查詢到。
- 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)瀏覽器解析伺服器返回的響應資料包,生成響應的結果。
Servlet訪問的過程:
Http請求---->web.xml--------> url -pattern----->servlet-name----->servlet-class-----> QuickStratServlet(對應的Class檔案)
Servlet的生命週期
Servlet生命週期可分為5個步驟
- 載入Servlet。當Tomcat第一次訪問Servlet的時候,Tomcat會負責建立Servlet的例項
- 初始化。當Servlet被例項化後,Tomcat會呼叫init()方法初始化這個物件
- 處理服務。當瀏覽器訪問Servlet的時候,Servlet 會呼叫service()方法處理請求
- 銷燬。當Tomcat關閉時或者檢測到Servlet要從Tomcat刪除的時候會自動呼叫destroy()方法,讓該例項釋放掉所佔的資源。一個Servlet如果長時間不被使用的話,也會被Tomcat自動銷燬
- 解除安裝。當Servlet呼叫完destroy()方法後,等待垃圾回收。如果有需要再次使用這個Servlet,會重新呼叫init()方法進行初始化操作。
簡單總結:只要訪問Servlet,service()就會被呼叫。init()只有第一次訪問Servlet的時候才會被呼叫。 destroy()只有在Tomcat關閉的時候才會被呼叫。
Servlet的配置
使用配置檔案
- 寫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);
}
}
複製程式碼
- 在web.xml中配置Servlet ######使用springboot
- 寫servlet類
- 在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了。其匹配規則和順序如下:
- 精確路徑匹配。例子:比如servletA 的url-pattern為 /test,servletB的url-pattern為 /* ,這個時候,如果我訪問的url為http://localhost/test ,這個時候容器就會先進行精確路徑匹配,發現/test正好被servletA精確匹配,那麼就去呼叫servletA,也不會去理會其他的servlet了。
- 最長路徑匹配。例子:servletA的url-pattern為/test/,而servletB的url-pattern為/test/a/,此時訪問http://localhost/test/a時,容器會選擇路徑最長的servlet來匹配,也就是這裡的servletB。
- 擴充套件匹配。 如果url最後一段包含擴充套件,容器將會根據擴充套件選擇合適的servlet。例子:servletA的url-pattern:*.action
- 最後, 如果前面三條規則都沒有找到一個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
- 實現 SingleThreadModel 介面
- 使用synchronized同步對共享資料的操作
- 避免使用例項變數
對上面的三種方法進行測試,可以表明用它們都能設計出執行緒安全的Servlet程式。但是,如果一個Servlet實現了SingleThreadModel介面,Servlet引擎將為每個新的請求建立一個單獨的Servlet例項,這將引起大量的系統開銷。SingleThreadModel在Servlet2.4中已不再提倡使用;同樣如果在程式中使用同步來保護要使用的共享的資料,也會使系統的效能大大下降。這是因為被同步的程式碼塊在同一時刻只能有一個執行緒執行它,使得其同時處理客戶請求的吞吐量降低,而且很多客戶處於阻塞狀態。另外為保證主存內容和執行緒的工作記憶體中的資料的一致性,要頻繁地重新整理快取,這也會大大地影響系統的效能。所以在實際的開發中也應避免或最小化 Servlet 中的同步程式碼;**在Serlet中避免使用例項變數是保證Servlet執行緒安全的最佳選擇。**從Java 記憶體模型也可以知道,方法中的臨時變數是在棧上分配空間,而且每個執行緒都有自己私有的棧空間,所以它們不會影響執行緒的安全
-
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,無法訪問到靜態資源。)