天天看點

卷妹的成長日記之javaweb day5

卷妹的成長日記之javaweb day5

👩‍💻部落格首頁:京與舊鋪的部落格首頁

✨歡迎關注🖱點贊🎀收藏⭐留言✒

🔮本文由京與舊鋪原創,

😘系列專欄:java學習

👕參考網站:動力節點

💻首發時間:🎞2022年8月10日🎠

🎨你做三四月的事,八九月就會有答案,一起加油吧

🀄如果覺得部落客的文章還不錯的話,請三連支援一下部落客哦

🛒導航小助手🎪

文章目錄

  • ​​卷妹的成長日記之javaweb day5​​
  • ​​🛒導航小助手🎪​​
  • ​​模闆方法設計模式​​
  • ​​HttpServlet源碼分析​​
  • HTTP響應協定的具體封包:
HTTP/1.1 200 ok                                     狀态行
Content-Type: text/html;charset=UTF-8               響應頭
Content-Length: 160
Date: Mon, 08 Nov 2021 13:19:32 GMT
Keep-Alive: timeout=20
Connection: keep-alive
                                                    空白行
<!doctype html>                                     響應體
<html>
    <head>
        <title>from get servlet</title>
    </head>
    <body>
        <h1>from get servlet</h1>
    </body>
</html>      
  • 狀态行
  • 三部分組成
  • 第一部分:協定版本号(HTTP/1.1)
  • 第二部分:狀态碼(HTTP協定中規定的響應狀态号。不同的響應結果對應不同的号碼。)
  • 200 表示請求響應成功,正常結束。
  • 404表示通路的資源不存在,通常是因為要麼是你路徑寫錯了,要麼是路徑寫對了,但是伺服器中對應的資源并沒有啟動成功。總之404錯誤是前端錯誤。
  • 405表示前端發送的請求方式與後端請求的處理方式不一緻時發生:
  • 比如:前端是POST請求,後端的處理方式按照get方式進行處理時,發生405
  • 比如:前端是GET請求,後端的處理方式按照post方式進行處理時,發生405
  • 500表示伺服器端的程式出現了異常。一般會認為是伺服器端的錯誤導緻的。
  • 以4開始的,一般是浏覽器端的錯誤導緻的。
  • 以5開始的,一般是伺服器端的錯誤導緻的。
  • 第三部分:狀态的描述資訊
  • ok 表示正常成功結束。
  • not found 表示資源找不到。
  • 響應頭:
  • 響應的内容類型
  • 響應的内容長度
  • 響應的時間
  • 空白行:
  • 用來分隔“響應頭”和“響應體”的。
  • 響應體:
  • 響應體就是響應的正文,這些内容是一個長的字元串,這個字元串被浏覽器渲染,解釋并執行,最終展示出效果。
  • 怎麼檢視的協定内容?
  • 使用chrome浏覽器:F12。然後找到network,通過這個面闆可以檢視協定的具體内容。
  • 怎麼向伺服器發送GET請求,怎麼向伺服器發送POST請求?
  • 到目前為止,隻有一種情況可以發送POST請求:使用form表單,并且form标簽中的method屬性值為:method=“post”。
  • 其他所有情況一律都是get請求:
  • 在浏覽器位址欄上直接輸入URL,敲回車,屬于get請求。
  • 在浏覽器上直接點選超連結,屬于get請求。
  • 使用form表單送出資料時,form标簽中沒有寫method屬性,預設就是get
  • 或者使用form的時候,form标簽中method屬性值為:method=“get”
  • GET請求和POST請求有什麼差別?
  • get請求發送資料的時候,資料會挂在URI的後面,并且在URI後面添加一個“?”,"?"後面是資料。這樣會導緻發送的資料回顯在浏覽器的位址欄上。(get請求在“請求行”上發送資料)
  • ​​http://localhost:8080/servlet05/getServlet?username=zhangsan&userpwd=1111​​
  • post請求發送資料的時候,在請求體當中發送。不會回顯到浏覽器的位址欄上。也就是說post發送的資料,在浏覽器位址欄上看不到。(post在“請求體”當中發送資料)
  • get請求隻能發送普通的字元串。并且發送的字元串長度有限制,不同的浏覽器限制不同。這個沒有明确的規範。
  • get請求無法發送大資料量。
  • post請求可以發送任何類型的資料,包括普通字元串,流媒體等資訊:視訊、聲音、圖檔。
  • post請求可以發送大資料量,理論上沒有長度限制。
  • get請求在W3C中是這樣說的:get請求比較适合從伺服器端擷取資料。
  • post請求在W3C中是這樣說的:post請求比較适合向伺服器端傳送資料。
  • get請求是安全的。get請求是絕對安全的。為什麼?因為get請求隻是為了從伺服器上擷取資料。不會對伺服器造成威脅。(get本身是安全的,你不要用錯了。用錯了之後又冤枉人家get不安全,你這樣不好(太壞了),那是你自己的問題,不是get請求的問題。)
  • post請求是危險的。為什麼?因為post請求是向伺服器送出資料,如果這些資料通過後門的方式進入到伺服器當中,伺服器是很危險的。另外post是為了送出資料,是以一般情況下攔截請求的時候,大部分會選擇攔截(監聽)post請求。
  • get請求支援緩存。
  • ​​https://n.sinaimg.cn/finance/590/w240h350/20211101/b40c-b425eb67cabc342ff5b9dc018b4b00cc.jpg​​
  • 任何一個get請求最終的“響應結果”都會被浏覽器緩存起來。在浏覽器緩存當中:
  • 一個get請求的路徑a 對應 一個資源。
  • 一個get請求的路徑b 對應 一個資源。
  • 一個get請求的路徑c 對應 一個資源。
  • 實際上,你隻要發送get請求,浏覽器做的第一件事都是先從本地浏覽器緩存中找,找不到的時候才會去伺服器上擷取。這種緩存機制目的是為了提高使用者的體驗。
  • 有沒有這樣一個需求:我們不希望get請求走緩存,怎麼辦?怎麼避免走緩存?我希望每一次這個get請求都去伺服器上找資源,我不想從本地浏覽器的緩存中取。
  • 隻要每一次get請求的請求路徑不同即可。
  • ​​https://n.sinaimg.cn/finance/590/w240h350/20211101/7cabc342ff5b9dc018b4b00cc.jpg?t=789789787897898​​
  • ​​https://n.sinaimg.cn/finance/590/w240h350/20211101/7cabc342ff5b9dc018b4b00cc.jpg?t=789789787897899​​
  • ​​https://n.sinaimg.cn/finance/590/w240h350/20211101/7cabc342ff5b9dc018b4b00cc.jpg?t=系統毫秒數​​
  • 怎麼解決?可以在路徑的後面添加一個每時每刻都在變化的“時間戳”,這樣,每一次的請求路徑都不一樣,浏覽器就不走緩存了。
  • post請求不支援緩存。(POST是用來修改伺服器端的資源的。)
  • post請求之後,伺服器“響應的結果”不會被浏覽器緩存起來。因為這個緩存沒有意義。
  • GET請求和POST請求如何選擇,什麼時候使用GET請求,什麼時候使用POST請求?
  • 怎麼選擇GET請求和POST請求呢?衡量标準是什麼呢?你這個請求是想擷取伺服器端的資料,還是想向伺服器發送資料。如果你是想從伺服器上擷取資源,建議使用GET請求,如果你這個請求是為了向伺服器送出資料,建議使用POST請求。
  • 大部分的form表單送出,都是post方式,因為form表單中要填寫大量的資料,這些資料是收集使用者的資訊,一般是需要傳給伺服器,伺服器将這些資料儲存/修改等。
  • 如果表單中有敏感資訊,還是建議适用post請求,因為get請求會回顯敏感資訊到浏覽器位址欄上。(例如:密碼資訊)
  • 做檔案上傳,一定是post請求。要傳的資料不是普通文本。
  • 其他情況都可以使用get請求。
  • 不管你是get請求還是post請求,發送的請求資料格式是完全相同的,隻不過位置不同,格式都是統一的:
  • name=value&name=value&name=value&name=value
  • name是什麼?
  • 以form表單為例:form表單中input标簽的name。
  • value是什麼?
  • 以form表單為例:form表單中input标簽的value。

模闆方法設計模式

  • 什麼是設計模式?
  • 某個問題的固定的解決方案。(可以被重複使用。)
  • 你知道哪些設計模式?
  • GoF設計模式:
  • 通常我們所說的23種設計模式。(Gang of Four:4人組提出的設計模式)
  • 單例模式
  • 工廠模式
  • 代理模式
  • 門面模式
  • 責任鍊設計模式
  • 觀察者模式
  • 模闆方法設計模式
  • JavaEE設計模式:
  • DAO
  • DTO
  • VO
  • PO
  • pojo
  • 什麼是模闆方法設計模式?
  • 在模闆類的模闆方法當中定義核心算法骨架,具體的實作步驟可以延遲到子類當中完成。
  • 模闆類通常是一個抽象類,模闆類當中的模闆方法定義核心算法,這個方法通常是final的(但也可以不是final的)
  • 模闆類當中的抽象方法就是不确定實作的方法,這個不确定怎麼實作的事兒交給子類去做。

HttpServlet源碼分析

  • HttpServlet類是專門為HTTP協定準備的。比GenericServlet更加适合HTTP協定下的開發。
  • HttpServlet在哪個包下?
  • jakarta.servlet.http.HttpServlet
  • 到目前為止我們接觸了servlet規範中哪些接口?
  • jakarta.servlet.Servlet 核心接口(接口)
  • jakarta.servlet.ServletConfig Servlet配置資訊接口(接口)
  • jakarta.servlet.ServletContext Servlet上下文接口(接口)
  • jakarta.servlet.ServletRequest Servlet請求接口(接口)
  • jakarta.servlet.ServletResponse Servlet響應接口(接口)
  • jakarta.servlet.ServletException Servlet異常(類)
  • jakarta.servlet.GenericServlet 标準通用的Servlet類(抽象類)
  • http包下都有哪些類和接口呢?jakarta.servlet.http.*;
  • jakarta.servlet.http.HttpServlet (HTTP協定專用的Servlet類,抽象類)
  • jakarta.servlet.http.HttpServletRequest (HTTP協定專用的請求對象)
  • jakarta.servlet.http.HttpServletResponse (HTTP協定專用的響應對象)
  • HttpServletRequest對象中封裝了什麼資訊?
  • HttpServletRequest,簡稱request對象。
  • HttpServletRequest中封裝了請求協定的全部内容。
  • Tomcat伺服器(WEB伺服器)将“請求協定”中的資料全部解析出來,然後将這些資料全部封裝到request對象當中了。
  • 也就是說,我們隻要面向HttpServletRequest,就可以擷取請求協定中的資料。
  • HttpServletResponse對象是專門用來響應HTTP協定到浏覽器的。
  • 回憶Servlet生命周期?
  • 使用者第一次請求
  • Tomcat伺服器通過反射機制,調用無參數構造方法。建立Servlet對象。(web.xml檔案中配置的Servlet類對應的對象。)
  • Tomcat伺服器調用Servlet對象的init方法完成初始化。
  • Tomcat伺服器調用Servlet對象的service方法處理請求。
  • 使用者第二次請求
  • Tomcat伺服器調用Servlet對象的service方法處理請求。
  • 使用者第三次請求
  • Tomcat伺服器調用Servlet對象的service方法處理請求。
  • Tomcat伺服器調用Servlet對象的service方法處理請求。
  • 伺服器關閉
  • Tomcat伺服器調用Servlet對象的destroy方法,做銷毀之前的準備工作。
  • Tomcat伺服器銷毀Servlet對象。
  • HttpServlet源碼分析:
public class HelloServlet extends HttpServlet {
    // 使用者第一次請求,建立HelloServlet對象的時候,會執行這個無參數構造方法。
    public HelloServlet() {
    }
    
    //override 重寫 doGet方法
    //override 重寫 doPost方法
}

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.Serializable {
           
    // 使用者第一次請求的時候,HelloServlet對象第一次被建立之後,這個init方法會執行。
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    // 使用者第一次請求的時候,帶有參數的init(ServletConfig config)執行之後,會執行這個沒有參數的init()
    public void init() throws ServletException {
        // NOOP by default
    }
}

// HttpServlet模闆類。
public abstract class HttpServlet extends GenericServlet {
    // 使用者發送第一次請求的時候這個service會執行
    // 使用者發送第N次請求的時候,這個service方法還是會執行。
    // 使用者隻要發送一次請求,這個service方法就會執行一次。
    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            // 将ServletRequest和ServletResponse向下轉型為帶有Http的HttpServletRequest和HttpServletResponse
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        // 調用重載的service方法。
        service(request, response);
    }
    
    // 這個service方法的兩個參數都是帶有Http的。
    // 這個service是一個模闆方法。
    // 在該方法中定義核心算法骨架,具體的實作步驟延遲到子類中去完成。
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        // 擷取請求方式
        // 這個請求方式最終可能是:""
        // 注意:request.getMethod()方法擷取的是請求方式,可能是七種之一:
        // GET POST PUT DELETE HEAD OPTIONS TRACE
        String method = req.getMethod();

        // 如果請求方式是GET請求,則執行doGet方法。
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            // 如果請求方式是POST請求,則執行doPost方法。
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
    
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
        // 報405錯誤
        String msg = lStrings.getString("http.method_get_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        // 報405錯誤
        String msg = lStrings.getString("http.method_post_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
}

/*
通過以上源代碼分析:
    假設前端發送的請求是get請求,後端程式員重寫的方法是doPost
    假設前端發送的請求是post請求,後端程式員重寫的方法是doGet
    會發生什麼呢?
        發生405這樣的一個錯誤。
        405表示前端的錯誤,發送的請求方式不對。和伺服器不一緻。不是伺服器需要的請求方式。
    
    通過以上源代碼可以知道:隻要HttpServlet類中的doGet方法或doPost方法執行了,必然405.

怎麼避免405的錯誤呢?
    後端重寫了doGet方法,前端一定要發get請求。
    後端重寫了doPost方法,前端一定要發post請求。
    這樣可以避免405錯誤。
    
    這種前端到底需要發什麼樣的請求,其實應該後端說了算。後端讓發什麼方式,前端就得發什麼方式。
    
有的人,你會看到為了避免405錯誤,在Servlet類當中,将doGet和doPost方法都進行了重寫。
這樣,确實可以避免405的發生,但是不建議,405錯誤還是有用的。該報錯的時候就應該讓他報錯。
如果你要是同時重寫了doGet和doPost,那還不如你直接重寫service方法好了。這樣代碼還能
少寫一點。
*/      
  • 我們編寫的HelloServlet直接繼承HttpServlet,直接重寫HttpServlet類中的service()方法行嗎?
  • 可以,隻不過你享受不到405錯誤。享受不到HTTP協定專屬的東西。
  • 到今天我們終于得到了最終的一個Servlet類的開發步驟:
  • 第一步:編寫一個Servlet類,直接繼承HttpServlet
  • 第二步:重寫doGet方法或者重寫doPost方法,到底重寫誰,javaweb程式員說了算。
  • 第三步:将Servlet類配置到web.xml檔案當中。
  • 第四步:準備前端的頁面(form表單),form表單中指定請求路徑即可。