Servlet必會必知
“眨眼間,離上一篇寫技術博文時隔1個月。怕自己真的生疏了,都是備案太慢惹得。哈哈,繼續high~ ”
從[JavaEE 要懂的小事] Http相關 ,一直想寫點Web開發相關的。最近項目接口開發緊,還有準備新的九月份戰鬥。JDK IO源碼就隔一段落,溫故知新看看Servlet & JSP 相關。把自己基礎累積回顧一遍,並和大家分享分享一些心得和代碼。這裏應該涉及到一部分源碼,開發思想和一些手工做出的圖。喜歡java,或者有一定Java開發經驗的多提寶貴意見。
一、Web服務器
從事web開發的人,會很清楚一個東西叫HTTP服務器,比如JEE開發—Tomcat,Jetty,.NET開發—ISS等。HTTP服務器是使用 HTTP(超文本傳輸協議) 與客戶機瀏覽器進行信息交流。下麵就是HTTP服務器簡單交互圖:(來自[JavaEE 要懂的小事] Http相關 博客)
HTTP服務器是Web服務器的一種,也是開發最常見的,自然還有其他方式進行信息交互,比如FTP文件服務器…
Web服務器是可以向發出請求的瀏覽器提供文檔的程序。其核心過程為
連接過程 — 請求過程 — 應答過程 — 關閉連接
這讓我想到了Tomcat架構的一張圖:
二、Tomcat 簡單說幾句
如圖,Tomcat 包含了核心服務模塊:Connector連接模塊 和 Container 容器。Tomcat Server 核心是一個 Servlet/JSP Container。對每一個HTTP請求,過程如下
— 獲取連接
— Servlet來分析請求(HttpServletRequest)
— 調用其service方法,進行業務處理
— 產生相應的響應(HttpServletResponse)
— 關閉連接
如圖:
藍色線指向過程是請求,綠色線指向過程是響應過程。也就是上麵Web服務器核心過程:“連接過程 — 請求過程 — 應答過程 — 關閉連接”
三、我第一個Servlet
什麼是Servlet?(每次都會不停的問自己,這是什麼“What”?緊接著應該是什麼用“How”吧)
在 JavaEE 6文檔中,介紹如下
“Servlet 是運行在Web服務器的Java小程序。Servlet可以獲取並針對Web客戶端的請求作出響應。一般情況下,通過HTTP,即超文本傳輸協議,進行傳輸通信。”
A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol.
所以,Servlet 是Web服務器核心工作的抽象。它不單單隻是實現HttpServlet,可能實現有FtpServlet(這個我猜的)等。相對較多的Web開發,知道的肯定是HttpServlet。
在 JavaEE 6文檔中,是這樣介紹HttpServlet:
“HttpServlet 提供了一個能被繼承後創建一個適應Web網站的Http Servlet的抽象類。”
Provides an abstract class to be subclassed to create an HTTP servlet suitable for a Web site.
光說不練假把式,練一個“Hello,Servlet/JSP World!”:
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* * Copyright [2015] [Jeff Lee] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Jeff Lee * @since 2015-6-25 19:46:45 * HelloWrold案例 */ @WebServlet(urlPatterns = "/helloWorld.html") public class HelloWorldServletT extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ // 獲取輸出打印對象 PrintWriter out = resp.getWriter(); out.println("Hello,Servlet/JSP World!"); } }
右鍵該HelloWorldServletT.java文件 — Run As — Run On Server — 選擇Tomcat服務器 — Finish即可
等待片刻,你可看到網頁上如下輸出。這就是客戶端從HttpServlet獲取到的響應:
休息一下吧~ 看看小廣告:
開源代碼都在我的gitHub上哦 — https://github.com/JeffLi1993
三、分析源碼
@WebServlet(urlPatterns = "/helloWorld.html")
@WebServlet 注解用於聲明一個HttpServlet的配置。其中,urlPatters = “/helloWorld.html”,urlPatterns複數形式,說明至少一個URL必須被申明。它和另一個value必須存在一個,但不能同時存在。如果要匹配多個URL路徑的話,如下:
@WebServlet(urlPatterns = { "/helloWorld01.html", "/helloWorld02.html" }
下麵有個@Override,重寫了父類HttpServlet的doGet方法。我們先看看父類HttpServlet。HttpServlet是一個抽象類,它提供了以下方法:
— doGet , 服務於 HTPP GET 請求
— doPost , 服務於 HTTP POST 請求
— doPut , 服務於 HTTP PUT 請求
— doDelete,服務於 HTTP DELETE 請求
…
如圖:
對於不同的請求,HttpServlet的子類必須相應的實現至少一個方法,通常來說,會是其中一個,這樣代碼比較清晰。那父類的doGet方法做了什麼工作呢?
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String protocol = req.getProtocol(); String msg = lStrings.getString("http.method_get_not_supported"); if (protocol.endsWith("1.1")) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); } }
這裏就簡單的獲取了下HTTP協議及Http Local信息,然後可以協議是否是1.1,做出分別是405或者400HTTP狀態碼的響應。
回到HelloWorldServletT.java 這裏:
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 獲取輸出打印對象 PrintWriter out = resp.getWriter(); out.println("Hello,Servlet/JSP World!"); }
表示該HelloWorldServletT會接受Http GET請求,並OOM到HttpServletRequest,並執行裏麵的邏輯代碼和返回響應。 這裏從HttpServletResponse對象中獲取到輸出打印對象PrintWriter,然後輸出了“Hello,Servlet/JSP World!”。
完畢!哦還有一點補充補充補充:
print,這裏還好一句話。如果打印個table會很麻煩,因此有一個JSP的東西出現了,是Servlet的HTML化身。
五、深入Servlet 具體過程
又回到這個簡單的 Get Servlet代碼:
public class HelloWorldServletT extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ // 獲取輸出打印對象 PrintWriter out = resp.getWriter(); out.println("Hello,Servlet/JSP World!"); } }
這過程總結如下:
— 從瀏覽器(Client)獲取連接”/helloWorld.html”
— Tomcat Connector模塊將請求(Request)傳遞給 Container模塊
— Container 模塊會做以下事情
—— 分析HTPP請求信息,組裝成HttpServletRequest對象
—— 創建新的HttpServletResponse對象
—— 根據路由配置,搜索相應的Servlet,並創建一個線程用於處理本次請求。此時線程會將上麵Request和Response對象的索引,傳遞給Servlet
— 新線程中的Servlet處理邏輯
— 線程結束後,通過HttpServletResponse對象的PrintWriter,返回瀏覽器一個信息
過程圖如下:
藍色線指向過程是請求,綠色線指向過程是響應過程,橙色線指向過程是內部處理過程。
有些麵試題會這樣問:
Servlet是線程安全的嗎?
不是,一個servlet實現類隻會有一個實例對象,多個線程是可能會訪問同一個servlet實例對象的,線程安全問題都是由全局變量及靜態變量引起的。
因此,Servlet對象實例化是在以第一次請求此Servlet時,如果訪問後,實例對象存在內存中,隻會在服務器停止時,它才會消失。它不會隨著各個線程結束而結束。因此下次訪問Servlet時,Servlet Container會搜索相應的Servlet,如果不存在,Container新建相應的Servlet。這也是我們想要的結果。
六、小結
發現這一博客寫的太多,回頭一看。可以寫成三個文章了。言歸正傳本文要點如下
1、簡單介紹Web服務器 及 Tomcat容器
2、第一個Sevlet的開發及使用
3、深入源碼及api介紹使用
4、總結一次請求及響應的真實過程
5、歡迎點擊我的博客及GitHub — 博客提供RSS訂閱哦
———- https://www.bysocket.com/ ————- https://github.com/JeffLi1993 ———-
最後更新:2017-05-22 14:01:46