一、Servlet簡介
Servlet類最終開發步驟:
- 第一步:編寫一個Servlet類,直接繼承HttpServlet
- 第二步:重寫doGet方法或者doPost方法,重寫哪個我說的算!
- 第三步:將Servlet類配置到web.xml檔案當中
- 第四步:準備前端的頁面(form表單),指定請求路徑即可
- Servlet是一個介面,在它的下面有GenericServlet
和HttpServlet兩個實現類
我們來了解一下GenericServlet:
- 其中的GenericServlet實現了很多方法,只保留了一個抽象方法,就是我們經常用的service方法。
- 這或許就是為了更簡潔一點吧,我們每次不需要在像實現Servlet介面一樣,將全部的抽象方法都實現。
- 根據他的類圖可知,HttpServlet繼承了GenericServlet這個類,那麼它為什麼要繼承GenericServlet呢?
我們來了解一下HttpServlet:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
- 這段是HttpServlet中實現的Service方法的原始碼,我們可以清楚的瞭解到,Tomcat已經把這個Service寫好了,我們只需要去實現
doGet()
、doPost()
等等這些請求的方法就可以了。 - 因為這個Service會自己判斷,我們進行訪問的是什麼請求,然後自動找到相應的方法進行處理。
二、兩種設計模式
2.1介面卡模式
介面卡:介面卡就是一種適配中介軟體,它存在於不匹配的二者之間,用於連線二者,將不匹配變得匹配,簡單點理解就是平常所見的轉接頭,轉換器之類的存在。
public interface MyInterface {
void m1();
void m2();
void m3();
void m4();
void m5();
void test();
//但是這個介面中我們常用的方法只有test(),我們在實現此介面的時候,還需要實現其他方法,很累贅。
//所以我們就需要一個介面卡!
}
public abstract class Test implements MyInterface{
public void m1() {
}
public void m2() {
}
public void m3() {
}
public void m4() {
}
public void m5() {
}
//這是一個介面卡
//在建立一個介面的實現類,我們將常用的方法設定為抽象的方法
//這樣我們只需要繼承該類,然後實現方法即可
public abstract void test();
}
- 這樣做的優點就是下次我們只需要去繼承Test類,實現我們經常使用的test()方法就可以了。
- 使我們的減少了冗餘的程式碼量
- 同時你也會發現這個就和GenericServlet去繼承Servlet這個介面是一樣的
- 這就是介面卡設計模式,其實很簡單,也有利於我們更加理解抽象類的作用和介面的使用
public class Realize extends Test{
public void test() {
//這樣我們就不需要再去實現介面中的 其他方法了
}
}
2.2 模板設計方法模式
模板方法模式:是一種行為設計模式, 它在超類中定義了一個演算法的框架, 允許子類在不修改結構的情況下重寫演算法的特定步驟。
-
模板方法模式建議將演算法分解為一系列步驟, 然後將這些步驟改寫為方法, 最後在 “模板方法” 中依次呼叫這些方法。 步驟可以是 抽象的, 也可以有一些預設的實現。 為了能夠使用演算法, 客戶端需要自行提供子類並實現所有的抽象步驟。 如有必要還需重寫一些步驟 (但這一步中不包括模板方法自身)。
-
認真的去閱讀上面這段話:你就會發現他說將 演算法進行分解,最後在模板中依次呼叫:
- 此時的你是否想起我們剛開篇介紹的HttpServlet???
- HttpServlet繼承了 GenericServlet類,而GenericServlet類又實現了Servlet介面;HttpServlet裡面的Service()方法你是否還記得?
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
- 一大堆的
if
、else if
、else
這不就是所謂的將演算法分塊提煉了出來嗎?然後我們只需要去刻畫doGet()
、doPost()
等方法,程式的主框架是不變的,只是其中的細節需要我們自己去實現。 - 我個人目前覺得和介面卡模式比較相似,但略有不同之處。
三、Servlet物件的宣告週期
3.1 Servlet物件是由誰來維護的?
- Servlet物件的建立、以及物件上方法的呼叫、物件的銷燬這個過程,我們JavaWeb程式設計師是無權干預的。你可以仔細想想你什麼時候new出來過一個Servlet物件。
- Servlet物件的宣告週期是由Tomcat伺服器負責的
- Tomcat伺服器我們又可以稱之為:WEB容器
- WEB容器來管理Servlet物件的死活
3.2 Servlet認知強化
- 你還真可以自己new一個Servlet物件,但是並不受我們Tomcat管理,所以你自己new出來的Servlet物件,死活和Tomcat沒有關係。
- Tomcat建立的Servlet物件,這些Servlet都會被放到一個集合當中(HashMap),只有放到HashMap集合的Servlet才能夠被Tomcat容器管理。
- Tomcat容器底層應該有一個HashMap這樣的集合,在這個集合當中儲存了Servlet物件和請求路徑之間的關係,我想此時你更能夠想到為什麼我們總是在xml中如此定義?
- 我們可以將Servlet物件稱之為假單例模式,因為Servlet只有一個物件呀。?
- 配置在web.xml對應Servlet程式的
標籤下新增 <load-on-startup>1</load-on-startup>
配置,指定改Servlet物件在Web應用啟動時建立
3.3 關於Servlet中的方法
- 通過Servlet中的方法,我們就可以瞭解到它的生命週期、執行機制。
- 無引數構造方法、init方法只在第一次使用者傳送請求的時候執行
-
只要使用者傳送一次請求,service方法必然會被Tomcat呼叫一次。
-
關於Servlet類中方法的呼叫次數:
- 構造方法只執行一次
- init方法只執行一次
- service方法:使用者傳送n次請求執行n次
- destroy方法:只執行一次
什麼時候使用destroy方法:
- 通常在destroy方法當中,進行資源的關閉,馬上物件要被銷燬了,還有什麼沒有關閉的,抓緊時間關閉資源。
四、Servlet對映問題
- 只要是前端傳送的請求,就必須要寫專案名!
- 當我們進行轉發的時候需要加上"/":
request.getRequestDispatcher('/b');
- 當我們在web.xml中配置路徑時,需要加上
<url-pattern>/helloServlet</url-pattern>
url-pattern匹配規則:
- "/":弱的 全路徑匹配
- ".do":以特殊方式結尾的匹配
- "/*":強的 全路徑匹配
- "/hello/*":有字首的 所以匹配
五、HttpServletRequest
- 首先HttpServletRequest這個介面實現了ServletRequest介面,那麼他們兩個有什麼區別呢?
- HttpServletRequest:這個名字帶有http,所以它的裡面就有一些關於對瀏覽器的處理方法;例如
5.1 GET請求引數
● 在瀏覽器的url裡傳送,以查詢字串方式拼接引數(?key=value&key1=value1)
● 使用Ajax,設定請求型別為GET,在請求url後拼接查詢字串
● 用form表單傳送
/**
* GET,傳送的請求引數在請求行內,
* 請求行的資訊會由Tomcat自動解碼 (utf-8)
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HTTPServletOne doGet");
// 1 獲取請求引數
System.out.println("根據getParameter獲取請求引數值 :"+req.getParameter("name"));
// 2獲取所有引數名
Enumeration<String> paras = req.getParameterNames();
String s = null;
while (paras.hasMoreElements()) {
s = paras.nextElement();
System.out.println("根據getParameterNames得到的引數名:"+s +"引數值:"+ req.getParameter(s));
}
}
5.2 POST請求引數
● form表單傳輸,指定請求方式為POST,預設Content-Type為application/x-www-form-urlencoded
/**
* 傳送POST請求,請求引數都在請求體裡
* 請求體的內容由Servlet進行解析,預設編碼規範為ISO
* 必須指定請求頭 Content-Type
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HTTPServletOne doPost");
// 0 設定請求處理字符集
req.setCharacterEncoding("utf-8");
// 1.獲取請求引數
System.out.println("根據getParameter獲取請求引數值 :"+req.getParameter("name"));
}
● Ajax傳輸,指定請求方式為POST,必須新增Content-Type為application/x-www-form-urlencoded;charset=utf-8
/**
* 傳送POST請求,請求引數都在請求體裡
* 請求體的內容由Servlet進行解析,預設編碼規範為ISO
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HTTPServletOne doPost");
// 0 設定請求處理字符集
req.setCharacterEncoding("utf-8");
// 1 獲取JSON資料
// 2 reader輸入流, 是從請求體開始讀的
BufferedReader reader = req.getReader();
String s = null;
StringBuilder sb = new StringBuilder();
while ((s = reader.readLine()) != null) {
sb.append(s);
}
String json = sb.toString();
// 3 通過FastJson 工具進行解析
Person person = JSON.parseObject(json, Person.class);
System.out.println(person);
}
● 附上前端程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
function onGet() {
//ajax
var xhr = new XMLHttpRequest();
xhr.open("GET","http://localhost:8080/httpServlet01?name=123&age=12");
xhr.send();
}
function onPost() {
//ajax
//用GET方式傳送的請求引數在請求行內,請求行內的資訊會由Tomcat自動解碼(utf-8)
//如果用POST請求,所有引數都放在了請求體裡,
//但是請求體的內容不是Tomcat解析的, 是由Servlet進行解析的,預設的編碼規範為ISO
//則需要宣告請求頭格式:application/x-www-form-urlencoded
//表單傳送的話 會預設加上這個型別
var xhr = new XMLHttpRequest();
xhr.open("POST","http://localhost:8080/httpServlet01");
xhr.setRequestHeader("Content-Type","application/json;charset=utf-8")
xhr.send("{'name':'123'}");
}
function onPut() {
//ajax
var xhr = new XMLHttpRequest();
xhr.open("PUT","http://localhost:8080/httpServlet01");
xhr.send();
}
function onDelete() {
//ajax
var xhr = new XMLHttpRequest();
xhr.open("DELETE","http://localhost:8080/httpServlet01");
xhr.send();
}
</script>
</head>
<body>
<h1>Hello,Servlet!</h1>
<button onclick="onGet()">傳送GET請求</button>
<button onclick="onPost()">傳送POST請求</button>
<button onclick="onPut()">傳送PUT請求</button>
<button onclick="onDelete()">傳送DELETE請求</button>
</body>
</html>
六、結尾
- 對於Tomcat的Servlet內容就總結這麼多,若想深入學習等待後續更新。
- 我將會繼續更新關於Java方向的學習知識,感興趣的小夥伴可以關注一下。
- 文章寫得比較走心,用了很長時間,絕對不是copy過來的!
- 尊重每一位學習知識的人,同時也尊重每一位分享知識的人。
- ?你的點贊與關注,是我努力前行的無限動力。?