JavaWeb老杜視頻筆記總結,Servlet-JSP

關于直播

  1. 什么時間直播?

    • 晚上8:00到10:00

  2. 每周直播幾天?

    • 3天(周一、周三、周五)

    • 本周比較特殊:周四周五周六三天直播,從下周開始就是一三五直播。

  3. 直播什么內容?

    • 從JavaWEB開始。(Servlet為核心,從Servlet開始學習。)

    • JSP(JSP使用較少了,但是還有用,所以時間上少一些。快速地學習一下JSP。)

    • AJAX(異步通信技術。)

    • jQuery(JavaScript庫。)

    • MyBatis

    • Spring

    • SpringMVC

    • SpringBoot

    • SpringCloud

    • ....

需要提前準備了哪些技術,接下來的課才能聽懂?

  • JavaSE(Java語言的標準版,Java提供的最基本的類庫)

    • Java的開發環境搭建

    • Java的基礎語法

    • Java的面向對象

    • 數組

    • 常用類

    • 異常

    • 集合

    • 多線程

    • IO流

    • 反射機制

    • 注解Annotation

    • .....

  • MySQL(數據庫)

    • 最基本的要求是:能夠編寫增刪改查等簡單的SQL語句即可。

  • JDBC(Java語言鏈接數據庫)

    • 這是一套Java語言鏈接數據庫的接口。

  • WEB前端(會一些)

    • HTML(網頁)

    • CSS(層疊樣式表語言,修飾HTML)

    • JavaScript(一種腳本語言,運行在瀏覽器當中,讓瀏覽器中的元素可以增刪改。讓網頁產生更強的交互效果)

  • WEB后端

    • Servlet(Server Applet:服務器端的Java小程序)

    • JSP

    • AJAX

    • jQuery

    • MyBatis

    • Spring

    • SpringMVC

    • SpringBoot

    • SpringCloud

    • ......

Typora軟件介紹

  • Markdown文本編輯器(可以編輯xxx.md文件)

    public class Test{public static void main(String[] args){System.out.println("Test code!");}
    }

Servlet

關于系統架構

  1. 系統架構包括什么形式?

    • C/S架構

    • B/S架構

  2. C/S架構?

    • Client / Server(客戶端 / 服務器)

    • C/S架構的軟件或者說系統有哪些呢?

      • QQ(先去騰訊官網下載一個QQ軟件,幾十MB,然后把這個客戶端軟件安裝上去,然后輸入QQ號以及密碼,登錄之后,就可以和你的朋友聊天了,就可以使用這個軟件了。)

    • C/S架構的特點:需要安裝特定的客戶端軟件。

    • C/S架構的系統優點和缺點分別是什么?

      • 優點:

        • 速度快(軟件中的數據大部分都是集成到客戶端軟件當中的,很少量的數據從服務器端傳送過來,所以C/S結構的系統速度快)

        • 體驗好(速度又快,界面又酷炫,當然體驗好了。)

        • 界面酷炫(專門的語言去實現界面的,更加靈活。)

        • 服務器壓力小(因為大量的數據都是集成在客戶端軟件當中,所以服務器只需要傳送很少的數據量,當然服務器壓力小。)

        • 安全(因為大量的數據是集成在客戶端軟件當中的,并且客戶端有很多個,服務器雖然只有一個,就算服務器那邊地震了,火災了,服務器受損了,問題也不大,因為大量的數據在多個客戶端上有緩存,有存儲,所以從這個方面來說,C/S結構的系統比較安全。)

        • .....

      • 缺點:

        • 升級維護比較差勁。(升級維護比較麻煩。成本比較高。每一個客戶端軟件都需要升級。有一些軟件不是那么容易安裝的。)

  3. B/S架構?

    • B/S(Browser / Server,瀏覽器 / 服務器)

    • 百度一下,你就知道

    • 京東(JD.COM)-正品低價、品質保障、配送及時、輕松購物!

    • 126網易免費郵-你的專業電子郵局

    • B/S結構的系統是不是一個特殊的C/S系統?

      • 實際上B/S結構的系統還是一個C/S,只不過這個C比較特殊,這個Client是一個固定不變瀏覽器軟件。

    • B/S結構的系統優點和缺點是:

      • 優點:

        • 升級維護方便,成本比較低。(只需要升級服務器端即可。)

        • 不需要安裝特定的客戶端軟件,用戶操作極其方便。只需要打開瀏覽器,輸入網址即可。

      • 缺點:

        • 速度慢(不是因為帶寬低的問題,是因為所有的數據都是在服務器上,用戶發送的每一個請求都是需要服務器全身心的響應數據,所以B/S結構的系統在網絡中傳送的數據量比較大。)

        • 體驗差(界面不是那么酷炫,因為瀏覽器只支持三個語言HTML CSS JavaScript。在加上速度慢。)

        • 不安全(所有的數據都在服務器上,只要服務器發生火災,地震等不可抗力,最終數據全部丟失。)

        • ....

  4. C/S和B/S結構的系統,哪個好,哪個不好?

    • 這個問題問的沒有水平。并不是哪個好,哪個不好。不同結構的系統在不同的業務場景下有不同的適用場景。

    • 娛樂性軟件建議使用?

      • C/S 結構

    • 公司內部使用的一些業務軟件建議使用?

      • 公司內部使用的系統,需要維護成本低。

      • 公司內部使用的系統,不需要很酷炫。

      • 公司內部使用的企業級系統主要是能夠進行數據的維護即可。

      • B/S 結構。

  5. 注意了:開發B/S結構的系統,其實就是開發網站,其實就是開發一個WEB系統。

    • 開發一個WEB系統你需要會哪些技術?

      • WEB前端(運行在瀏覽器上的程序。)

        • HTML

        • CSS

        • JavaScript

      • WEB后端(WEB服務器端的程序。)

        • Java可以(Java做WEB開發我們稱為JavaWEB開發。JavaWEB開發最核心的規范:Servlet【Server Applet服務器端的Java小程序。】)

        • C語言也可以

        • C++也可以

        • Python也行

        • PHP也可以

        • ....

  6. JavaEE是什么?

    • Java包括三大塊:

      • JavaSE

        • Java標準版(一套類庫:別人寫好的一套類庫,只不過這個類庫是標準類庫,走EE,或者走ME,這個SE一定是基礎,先學。)

      • JavaEE(WEB方向,WEB系統。)

        • Java企業版(也是一套類庫:也是別人寫好的一套類庫,只不過這套類庫可以幫助我們完成企業級項目的開發,專門為企業內部提供解決方案的一套(多套)類庫。)

        • 別人寫好的,你用就行了,用它可以開發企業級項目。

        • 可以開發web系統。

        • Java比較火爆的就是這個JavaEE方向。

      • JavaME

        • Java微型版(還是一套類庫,只不過這套類庫幫助我們進行電子微型設備內核程序的開發)

        • 機頂盒內核程序,吸塵器內核程序,電冰箱內核程序,電飯煲內核程序。。。。。

    • JavaEE實際上包括很多種規范,13種規范,其中Servlet就是JavaEE規范之一。學Servlet還是Java語言。

B/S結構的系統通信原理(沒有涉及到Java小程序)

  • WEB系統的訪問過程

    • 第一步:打開瀏覽器

    • 第二步:找到地址欄

    • 第三步:輸入一個合法的網址

    • 第四步:回車

    • 第五步:在瀏覽器上會展示響應的結果。

  • 關于域名:

    • 百度一下,你就知道 (網址)

    • www.baidu.com 是一個域名

    • 在瀏覽器地址欄上輸入域名,回車之后,域名解析器會將域名解析出來一個具體的IP地址和端口號等。

    • 解析結果也許是:百度一下,你就知道

  • IP地址是啥?

    • 計算機在網絡當中的一個身份證號。在同一個網絡當中,IP地址是唯一的。

    • A計算機要想和B計算機通信,首先你需要知道B計算機的IP地址,有了IP地址才能建立連接。

  • 端口號是啥?

    • 一個端口代表一個軟件(一個端口代表一個應用,一個端口僅代表一個服務)。

    • 一個計算機當中有很多軟件,每一個軟件啟動之后都有一個端口號。

    • 在同一個計算機上,端口號具有唯一性。

  • 一個WEB系統的通信原理?通信步驟:

    • 第一步:用戶輸入網址(URL)

    • 第二步:域名解析器進行域名解析:百度一下,你就知道

    • 第三步:瀏覽器軟件在網絡中搜索110.242.68.3這一臺主機,直到找到這臺主機。

    • 第四步:定位110.242.68.3這臺主機上的服務器軟件,因為是80端口,可以很輕松的定位到80端口對應的服務器軟件。

    • 第五步:80端口對應的服務器軟件得知瀏覽器想要的資源名是:index.html

    • 第六步:服務器軟件找到index.html文件,并且將index.html文件中的內容直接輸出響應到瀏覽器上。

    • 第七步:瀏覽器接收到來自服務器的代碼(HTML CSS JS)

    • 第八步:瀏覽器渲染,執行HTML CSS JS代碼,展示效果。

  • 什么是URL?

    • 統一資源定位符(百度一下,你就知道)

  • 什么是請求,什么是響應?

    • 請求和響應實際上說的是數據的流向不同。

    • 從Browser端發送數據到Server端,我們稱為請求。英語單詞:request

    • 從Server端向瀏覽器Browser端發送數據,我們稱為響應。英語單詞:response

    • B --> S (請求request)

    • S --> B (響應response)

關于WEB服務器軟件

  • WEB服務器軟件都有哪些呢?(這些軟件都是提前開發好的。)

    • Tomcat(WEB服務器)

    • jetty(WEB服務器)

    • JBOSS(應用服務器)

    • WebLogic(應用服務器)

    • WebSphere(應用服務器)

  • 應用服務器和WEB服務器的關系?

    • 應用服務器實現了JavaEE的所有規范。(JavaEE有13個不同的規范。)

    • WEB服務器只實現了JavaEE中的Servlet + JSP兩個核心的規范。

    • 通過這個講解說明了:應用服務器是包含WEB服務器的。

    • 用過JBOSS服務器的同學應該很清楚,JBOSS中內嵌了一個Tomcat服務器。

  • Tomcat下載

    • apache官網地址:Welcome to The Apache Software Foundation!

    • tomcat官網地址:Apache Tomcat? - Welcome!

    • tomcat開源免費的輕量級WEB服務器。

    • tomcat還有另外一個名字:catalina(catalina是美國的一個島嶼,風景秀麗,據說作者是在這個風景秀麗的小島上開發了一個輕量級的WEB服務器,體積小,運行速度快,因此tomcat又被稱為catalina)

    • tomcat的logo是一只公貓(寓意表示Tomcat服務器是輕巧的,小巧的,果然,體積小,運行速度快,只實現了Servlet+JSP規范)

    • tomcat是java語言寫的。

    • tomcat服務器要想運行,必須先又jre(Java的運行時環境)

  • Tomcat服務器要想運行,需要先有jre,所以要先安裝JDK,配置java運行環境。

    • JAVA_HOME=C:\Program Files\Java\jdk-17.0.1

    • PATH=%JAVA_HOME%\bin

    • 目前JAVA_HOME沒有配置,思考一個問題,這樣行不行呢?目前只運行java程序是沒問題的。真的沒問題嗎?

  • Tomcat服務器的安裝:

    • 綠色版本的安裝很簡單,直接zip包解壓即可。解壓就是安裝。

    • 我有一個好習慣,在C盤的根目錄下新建一個dev目錄,java開發所有相關的工具都安裝到dev目錄下,這樣比較方便管理。(你隨意)

    • 啟動Tomcat

      • bin目錄下有一個文件:startup.bat,通過它可以啟動Tomcat服務器。

        • xxx.bat文件是個什么文件?bat文件是windows操作系統專用的,bat文件是批處理文件,這種文件中可以編寫大量的windows的dos命令,然后執行bat文件就相當于批量的執行dos命令。

        • startup.sh,這個文件在windows當中無法執行,在Linux環境當中可以使用。在Linux環境下能夠執行的是shell命令,大量的shell命令編寫在shell文件當中,然后執行這個shell文件可以批量的執行shell命令。

        • tomcat服務器提供了bat和sh文件,說明了這個tomcat服務器的通用性。

        • 分析startup.bat文件得出,執行這個命令,實際上最后是執行:catalina.bat文件。

        • catalina.bat文件中有這樣一行配置:MAINCLASS=org.apache.catalina.startup.Bootstrap (這個類就是main方法所在的類。)

        • tomcat服務器就是Java語言寫的,既然是java語言寫的,那么啟動Tomcat服務器就是執行main方法。

      • 我們嘗試打開dos命令窗口,在dos命令窗口中輸入startup.bat來啟動tomcat服務器。

      • 啟動Tomcat服務器只配置path對應的bin目錄是不行的。有兩個環境變量需要配置:

        • JAVA_HOME=JDK的根

        • CATALINA_HOME=Tomcat服務器的根

  • 關于Tomcat服務器的目錄

    • bin : 這個目錄是Tomcat服務器的命令文件存放的目錄,比如:啟動Tomcat,關閉Tomcat等。

    • conf: 這個目錄是Tomcat服務器的配置文件存放目錄。(server.xml文件中可以配置端口號,默認Tomcat端口是8080)

    • lib :這個目錄是Tomcat服務器的核心程序目錄,因為Tomcat服務器是Java語言編寫的,這里的jar包里面都是class文件。

    • logs: Tomcat服務器的日志目錄,Tomcat服務器啟動等信息都會在這個目錄下生成日志文件。

    • temp:Tomcat服務器的臨時目錄。存儲臨時文件。

    • webapps:這個目錄當中就是用來存放大量的webapp(web application:web應用)

    • work:這個目錄是用來存放JSP文件翻譯之后的java文件以及編譯之后的class文件。

  • 配置Tomcat服務器需要哪些環境變量?

    • JAVA_HOME=JDK的根

    • CATALINA_HOME=Tomcat服務器的根

    • PATH=%JAVA_HOME%\bin;%CATALINA_HOME%\bin

  • 啟動Tomcat: startup

  • 關閉Tomcat:stop (shutdown.bat文件重命名為stop.bat,為什么?原因是shutdown命令和windows中的關機命令沖突。所以修改一下。)

  • 怎么測試Tomcat服務器有沒有啟動成功呢?

    • 打開瀏覽器,在瀏覽器的地址欄上輸入URL即可:

      • http://ip地址:端口號

      • ip地址是什么?端口號我知道,是8080

      • 本機的IP地址是:127.0.0.1,或者是localhost,都行。

實現一個最基本的web應用(這個web應用中沒有java小程序)

  • 第一步:找到CATALINA_HOME\webapps目錄

    • 因為所有的webapp要放到webapps目錄下。(沒有為什么,這是Tomcat服務器的要求。如果不放到這里,Tomcat服務器找不到你的應用。)

  • 第二步:在CATALINA_HOME\webapps目錄下新建一個子目錄,起名:oa

    • 這個目錄名oa就是你這個webapp的名字。

  • 第三步:在oa目錄下新建資源文件,例如:index.html

    • 編寫index.html文件的內容。

  • 第四步:啟動Tomcat服務器

  • 第五步:打開瀏覽器,在瀏覽器地址欄上輸入這樣的URL:

  • http://127.0.0.1:8080/oa/index.html

  • 思考一個問題:

    • 我們在瀏覽器上直接輸入一個URL,然后回車。這個動作和超鏈接一樣嗎?既然是一樣的,我們完全可以使用超鏈接。

      <!--注意以下的路徑,以/開始,帶項目名,是一個絕對路徑。不需要添加:http://127.0.0.1:8080-->
      <a href="/oa/login.html">user login2</a>
      ?
      <!--多個層級也沒有關系,正常訪問即可。-->
      <!--注意:我們目前前端上的路徑都以“/”開始的,都是加項目名的。-->
      <a href="/oa/test/debug/d.html">d page</a>
  • http://127.0.0.1:8080/oa/userList.html

    • 訪問這個地址,可以展示一個用戶列表頁面。但是這個用戶列表頁面是寫死在HTML文件當中的。這種資源我們稱為靜態資源。怎么能變成動態資源。顯然需要連接數據庫。

    • 連接數據庫需要JDBC程序,也就是說需要編寫Java程序連接數據庫,數據庫中有多少條記錄,頁面上就顯示多少條記錄,這種技術被稱為動態網頁技術。(動態網頁技術并不是說頁面中有flash動畫。動態網頁技術是說頁面中的數據是動態的,根據數據庫中數據的變化而變化。)

對于一個動態的web應用來說,一個請求和響應的過程有多少個角色參與,角色和角色之間有多少個協議

  • 有哪些角色(在整個BS結構的系統當中,有哪些人參與進去了)

    • 瀏覽器軟件的開發團隊(瀏覽器軟件太多了:谷歌瀏覽器、火狐瀏覽器、IE瀏覽器....)

    • WEB Server的開發團隊(WEB Server這個軟件也是太多了:Tomcat、Jetty、WebLogic、JBOSS、WebSphere....)

    • DB Server的開發團隊(DB Server這個軟件也是太多了:Oracle、MySQL.....)

    • webapp的開發團隊(WEB應用是我們做為JavaWEB程序員開發的)

  • 角色和角色之間需要遵守哪些規范,哪些協議

    • webapp的開發團隊 和 WEB Server的開發團隊 之間有一套規范: JavaEE規范之一Servlet規范。

      • Servlet規范的作用是什么?

        • WEB Server 和 webapp解耦合。

    • Browser 和 WebServer之間有一套傳輸協議:HTTP協議。(超文本傳輸協議。)

    • webapp開發團隊 和 DB Server的開發團隊之間有一套規范:JDBC規范。

  • Servlet規范是一個什么規范?

    • 遵循Servlet規范的webapp,這個webapp就可以放在不同的WEB服務器中運行。(因為這個webapp是遵循Servlet規范的。)

    • Servlet規范包括什么呢?

      • 規范了哪些接口

      • 規范了哪些類

      • 規范了一個web應用中應該有哪些配置文件

      • 規范了一個web應用中配置文件的名字

      • 規范了一個web應用中配置文件存放的路徑

      • 規范了一個web應用中配置文件的內容

      • 規范了一個合法有效的web應用它的目錄結構應該是怎樣的。

      • .....

開發一個帶有Servlet(Java小程序)的webapp(重點)

  • 開發步驟是怎樣的?

    • 第一步:在webapps目錄下新建一個目錄,起名crm(這個crm就是webapp的名字)。當然,也可以是其它項目,比如銀行項目,可以創建一個目錄bank,辦公系統可以創建一個oa。

      • 注意:crm就是這個webapp的根

    • 第二步:在webapp的根下新建一個目錄:WEB-INF

      • 注意:這個目錄的名字是Servlet規范中規定的,必須全部大寫,必須一模一樣。必須的必須。

    • 第三步:在WEB-INF目錄下新建一個目錄:classes

      • 注意:這個目錄的名字必須是全部小寫的classes。這也是Servlet規范中規定的。另外這個目錄下一定存放的是Java程序編譯之后的class文件(這里存放的是字節碼文件)。

    • 第四步:在WEB-INF目錄下新建一個目錄:lib

      • 注意:這個目錄不是必須的。但如果一個webapp需要第三方的jar包的話,這個jar包要放到這個lib目錄下,這個目錄的名字也不能隨意編寫,必須是全部小寫的lib。例如java語言連接數據庫需要數據庫的驅動jar包。那么這個jar包就一定要放到lib目錄下。這Servlet規范中規定的。

    • 第五步:在WEB-INF目錄下新建一個文件:web.xml

      • 注意:這個文件是必須的,這個文件名必須叫做web.xml。這個文件必須放在這里。一個合法的webapp,web.xml文件是必須的,這個web.xml文件就是一個配置文件,在這個配置文件中描述了請求路徑和Servlet類之間的對照關系。

      • 這個文件最好從其他的webapp中拷貝,最好別手寫。沒必要。復制粘貼

      • <?xml version="1.0" encoding="UTF-8"?>
        ?
        <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaeehttps://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"version="5.0"metadata-complete="true">
        ?
        ?
        </web-app>
        ?
    • 第六步:編寫一個Java程序,這個小Java程序也不能隨意開發,這個小java程序必須實現Servlet接口。

      • 這個Servlet接口不在JDK當中。(因為Servlet不是JavaSE了。Servlet屬于JavaEE,是另外的一套類庫。)

      • Servlet接口(Servlet.class文件)是Oracle提供的。(最原始的是sun公司提供的。)

      • Servlet接口是JavaEE的規范中的一員。

      • Tomcat服務器實現了Servlet規范,所以Tomcat服務器也需要使用Servlet接口。Tomcat服務器中應該有這個接口,Tomcat服務器的CATALINA_HOME\lib目錄下有一個servlet-api.jar,解壓這個servlet-api.jar之后,你會看到里面有一個Servlet.class文件。

      • 重點:從JakartaEE9開始,Servlet接口的全名變了:jakarta.servlet.Servlet

      • 注意:編寫這個Java小程序的時候,java源代碼你愿意在哪里就在哪里,位置無所謂,你只需要將java源代碼編譯之后的class文件放到classes目錄下即可。

    • 第七步:編譯我們編寫的HelloServlet

      • 重點:你怎么能讓你的HelloServlet編譯通過呢?配置環境變量CLASSPATH

        CLASSPATH=.;C:\dev\apache-tomcat-10.0.12\lib\servlet-api.jar

      • 思考問題:以上配置的CLASSPATH和Tomcat服務器運行有沒有關系?

        • 沒有任何關系,以上配置這個環境變量只是為了讓你的HelloServlet能夠正常編譯生成class文件。

    • 第八步:將以上編譯之后的HelloServlet.class文件拷貝到WEB-INF\classes目錄下。

    • 第九步:在web.xml文件中編寫配置信息,讓“請求路徑”和“Servlet類名”關聯在一起。

      • 這一步用專業術語描述:在web.xml文件中注冊Servlet類。

      • <?xml version="1.0" encoding="UTF-8"?>
        ?
        <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaeehttps://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"version="5.0"metadata-complete="true">
        ?<!--servlet描述信息--><!--任何一個servlet都對應一個servlet-mapping --><servlet><servlet-name>fdsafdsagfdsafdsa</servlet-name><!--這個位置必須是帶有包名的全限定類名--><servlet-class>com.bjpowernode.servlet.HelloServlet</servlet-class></servlet>
        ?<!--servlet映射信息--><servlet-mapping><!--這個也是隨便的,不過這里寫的內容要和上面的一樣。--><servlet-name>fdsafdsagfdsafdsa</servlet-name><!--這里需要一個路徑--><!--這個路徑唯一的要求是必須以 / 開始--><!--當前這個路徑可以隨便寫--><url-pattern>/fdsa/fd/saf/d/sa/fd/sa/fd</url-pattern></servlet-mapping></web-app>
        ?

    • 第十步:啟動Tomcat服務器

    • 第十一步:打開瀏覽器,在瀏覽器地址欄上輸入一個url,這個URL必須是:

      • http://127.0.0.1:8080/crm/fdsa/fd/saf/d/sa/fd/sa/fd

      • 非常重要的一件事:瀏覽器上的請求路徑不能隨便寫,這個請求路徑必須和web.xml文件中的url-pattern一致。

      • 注意:瀏覽器上的請求路徑和web.xml文件中的url-pattern的唯一區別就是:瀏覽器上的請求路徑帶項目名:/crm

    • 瀏覽器上編寫的路徑太復雜,可以使用超鏈接。(非常重要:html頁面只能放到WEB-INF目錄外面。

    • 以后不需要我們編寫main方法了。tomcat服務器負責調用main方法,Tomcat服務器啟動的時候執行的就是main方法。我們javaweb程序員只需要編寫Servlet接口的實現類,然后將其注冊到web.xml文件中,即可。

    • 總結一下:一個合法的webapp目錄結構應該是怎樣的?

      webapproot|------WEB-INF|------classes(存放字節碼)|------lib(第三方jar包)|------web.xml(注冊Servlet)|------html|------css|------javascript|------image....
    • 瀏覽器發送請求,到最終服務器調用Servlet中的方法,是怎樣的一個過程?(以下這個過程描述的很粗糙。其中還有很多步驟我省略了。)

      • 用戶輸入URL,或者直接點擊超鏈接:http://127.0.0.1:8080/crm/fdsa/fd/saf/d/sa/fd/sa/fd

      • 然后Tomcat服務器接收到請求,截取路徑:/crm/fdsa/fd/saf/d/sa/fd/sa/fd

      • Tomcat服務器找到crm項目

      • Tomcat服務器在web.xml文件中查找/fdsa/fd/saf/d/sa/fd/sa/fd 對應的Servlet是:com.bjpowernode.servlet.HelloServlet

      • Tomcat服務器通過反射機制,創建com.bjpowernode.servlet.HelloServlet的對象。

      • Tomcat服務器調用com.bjpowernode.servlet.HelloServlet對象的service方法。

關于JavaEE的版本

  • JavaEE目前最高版本是 JavaEE8

  • JavaEE被Oracle捐獻了,Oracle將JavaEE規范捐獻給Apache了。

  • Apache把JavaEE換名了,以后不叫JavaEE了,以后叫做 jakarta EE。

  • 以后沒有JavaEE了。以后都叫做Jakarta EE。

  • JavaEE8版本升級之后的"JavaEE 9",不再是"JavaEE9"這個名字了,叫做JakartaEE9

  • JavaEE8的時候對應的Servlet類名是:javax.servlet.Servlet

  • JakartaEE9的時候對應的Servlet類名是:jakarta.servlet.Servlet (包名都換了)

  • 如果你之前的項目還是在使用javax.servlet.Servlet,那么你的項目無法直接部署到Tomcat10+版本上。你只能部署到Tomcat9-版本上。在Tomcat9以及Tomcat9之前的版本中還是能夠識別javax.servlet這個包。

解決Tomcat服務器在DOS命令窗口中的亂碼問題(控制臺亂碼)

將CATALINA_HOME/conf/logging.properties文件中的內容修改如下:

java.util.logging.ConsoleHandler.encoding = GBK

向瀏覽器響應一段HTML代碼

public void service(ServletRequest request, ServletResponse response){response.setContentType("text/html");PrintWriter out = response.getWriter();out.print("<h1>hello servlet!</h1>");
}

在Servlet中連接數據庫,怎么做?

  • Servlet是Java程序,所以在Servlet中完全可以編寫JDBC代碼連接數據庫。

  • 在一個webapp中去連接數據庫,需要將驅動jar包放到WEB-INF/lib目錄下。(com.mysql.cj.jdbc.Driver 這個類就在驅動jar包當中。)

在集成開發環境當中開發Servlet程序

  • 集成開發工具很多,其中目前使用比較多的是:

    • IntelliJ IDEA(這個居多,IDEA在提示功能方面要強于Eclipse,也就是說IDEA使用起來比Eclipse更加智能,更好用。JetBrain公司開發的。收費的。)

    • Eclipse(這個少一些),Eclipse目前還是有團隊使用,只不過處于減少的趨勢,自己從事工作之后,可能會遇到。Eclipse是IBM團隊開發的。Eclipse寓意是“日食”。“日食”表示將太陽吃掉。太陽是SUN。IBM團隊開發Eclipse的寓意是吞并SUN公司,但是2009年的時候SUN公司被Oracle公司并購了。IBM并沒有成功并購SUN公司。

  • 使用IDEA集成開發工具開發Servlet

    • 第一步:New Project(我比較習慣先創建一個Empty Project【空工程】,然后在空工程下新建Module【模塊】,這不是必須的,只是一種習慣,你可以直接新建非空的Project),這個Empty Project起名為:javaweb(不是必須的,只是一個名字而已。一般情況下新建的Project的名字最好和目錄的名字一致。)

    • 第二步:新建模塊(File --> new --> Module...)

      • 這里新建的是一個普通的JavaSE模塊(這里先不要新建Java Enterprise模塊)

      • 這個Module自動會被放在javaweb的project下面。

      • 這個Module起名:servlet01

    • 第三步:讓Module變成JavaEE的模塊。(讓Module變成webapp的模塊。符合webapp規范。符合Servlet規范的Module)

      • 在Module上點擊右鍵:Add Framework Support...(添加框架支持)

      • 在彈出的窗口中,選擇Web Application(選擇的是webapp的支持)

      • 選擇了這個webapp的支持之后,IDEA會自動給你生成一個符合Servlet規范的webpp目錄結構。

      • 重點,需要注意的:在IDEA工具中根據Web Application模板生成的目錄中有一個web目錄,這個目錄就代表webapp的根

    • 第四步(非必須):根據Web Application生成的資源中有index.jsp文件,這里我選擇刪除這個index.jsp文件。

    • 第五步:編寫Servlet(StudentServlet)

      • class StudentServlet implements Servlet

      • 這個時候發現Servlet.class文件沒有。怎么辦?將CATALINA_HOME/lib/servlet-api.jar和jsp-api.jar添加到classpath當中(這里的classpath說的是IDEA的classpath)

        • File --> Project Structrue --> Modules --> + 加號 --> Add JARS....

      • 實現jakarta.servlet.Servlet接口中的5個方法。

    • 第六步:在Servlet當中的service方法中編寫業務代碼(我們這里連接數據庫了。)

    • 第七步:在WEB-INF目錄下新建了一個子目錄:lib(這個目錄名可不能隨意,必須是全部小寫的lib),并且將連接數據庫的驅動jar包放到lib目錄下。

    • 第八步:在web.xml文件中完成StudentServlet類的注冊。(請求路徑和Servlet之間對應起來)

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0">
    ?<servlet><servlet-name>studentServlet</servlet-name><servlet-class>com.bjpowernode.javaweb.servlet.StudentServlet</servlet-class></servlet><servlet-mapping><servlet-name>studentServlet</servlet-name><url-pattern>/servlet/student</url-pattern></servlet-mapping></web-app>

    • 第九步:給一個html頁面,在HTML頁面中編寫一個超鏈接,用戶點擊這個超鏈接,發送請求,Tomcat執行后臺的StudentServlet。

      • student.html

      • 這個文件不能放到WEB-INF目錄里面,只能放到WEB-INF目錄外面。

      • student.html文件的內容

      • <!DOCTYPE html>
        <html lang="en">
        <head><meta charset="UTF-8"><title>student page</title>
        </head>
        <body><!--這里的項目名是 /xmm ,無法動態獲取,先寫死--><a href="/xmm/servlet/student">student list</a>
        </body>
        </html>

    • 第十步:讓IDEA工具去關聯Tomcat服務器。關聯的過程當中將webapp部署到Tomcat服務器當中。

      • IDEA工具右上角,綠色小錘子右邊有一個:Add Configuration

      • 左上角加號,點擊Tomcat Server --> local

      • 在彈出的界面中設置服務器Server的參數(基本上不用動)

      • 在當前窗口中有一個Deployment(點擊這個用來部署webapp),繼續點擊加號,部署即可。

      • 修改 Application context為:/xmm

    • 第十一步:啟動Tomcat服務器

      • 在右上角有綠色的箭頭,或者綠色的小蟲子,點擊這個綠色的小蟲子,可以采用debug的模式啟動Tomcat服務器。

      • 我們開發中建議適用debug模式啟動Tomcat

    • 第十二步:打開瀏覽器,在瀏覽器地址欄上輸入:http://localhost:8080/xmm/student.html

Servlet對象的生命周期

  • 什么是Servlet對象生命周期?

    • Servlet對象什么時候被創建。

    • Servlet對象什么時候被銷毀。

    • Servlet對象創建了幾個?

    • Servlet對象的生命周期表示:一個Servlet對象從出生在最后的死亡,整個過程是怎樣的。

  • Servlet對象是由誰來維護的?

    • Servlet對象的創建,對象上方法的調用,對象最終的銷毀,Javaweb程序員是無權干預的。

    • Servlet對象的生命周期是由Tomcat服務器(WEB Server)全權負責的。

    • Tomcat服務器通常我們又稱為:WEB容器。(這個叫法你要知道【WEB Container】)

    • WEB容器來管理Servlet對象的死活。

  • 思考:我們自己new的Servlet對象受WEB容器的管理嗎?

    • 我們自己new的Servlet對象是不受WEB容器管理的。

    • WEB容器創建的Servlet對象,這些Servlet對象都會被放到一個集合當中(HashMap),只有放到這個HashMap集合中的Servlet才能夠被WEB容器管理,自己new的Servlet對象不會被WEB容器管理。(自己new的Servlet對象不在容器當中)

    • web容器底層應該有一個HashMap這樣的集合,在這個集合當中存儲了Servlet對象和請求路徑之間的關系

  • 研究:服務器在啟動的Servlet對象有沒有被創建出來(默認情況下)?

    • 在Servlet中提供一個無參數的構造方法,啟動服務器的時候看看構造方法是否執行。

    • 經過測試得出結論:默認情況下,服務器在啟動的時候Servlet對象并不會被實例化。

    • 這個設計是合理的。用戶沒有發送請求之前,如果提前創建出來所有的Servlet對象,必然是耗費內存的,并且創建出來的Servlet如果一直沒有用戶訪問,顯然這個Servlet對象是一個廢物,沒必要先創建。

  • 怎么讓服務器啟動的時候創建Servlet對象呢?

    • 在servlet標簽中添加<load-on-startup>子標簽,在該子標簽中填寫整數,越小的整數優先級越高。

    • <servlet><servlet-name>aservlet</servlet-name><servlet-class>com.bjpowernode.javaweb.servlet.AServlet</servlet-class><load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping><servlet-name>aservlet</servlet-name><url-pattern>/a</url-pattern>
      </servlet-mapping>
  • Servlet對象生命周期

    • 默認情況下服務器啟動的時候AServlet對象并沒有被實例化

    • 用戶發送第一次請求的時候,控制臺輸出了以下內容:

      AServlet無參數構造方法執行了
      AServlet's init method execute!
      AServlet's service method execute!
    • 根據以上輸出內容得出結論:

      • 用戶在發送第一次請求的時候Servlet對象被實例化(AServlet的構造方法被執行了。并且執行的是無參數構造方法。)

      • AServlet對象被創建出來之后,Tomcat服務器馬上調用了AServlet對象的init方法。(init方法在執行的時候,AServlet對象已經存在了。已經被創建出來了。)

      • 用戶發送第一次請求的時候,init方法執行之后,Tomcat服務器馬上調用AServlet對象的service方法。

    • 用戶繼續發送第二次請求,控制臺輸出了以下內容:

      AServlet's service method execute!
    • 根據以上輸出結果得知,用戶在發送第二次,或者第三次,或者第四次請求的時候,Servlet對象并沒有新建,還是使用之前創建好的Servlet對象,直接調用該Servlet對象的service方法,這說明:

      • 第一:Servlet對象是單例的(單實例的。但是要注意:Servlet對象是單實例的,但是Servlet類并不符合單例模式。我們稱之為假單例。之所以單例是因為Servlet對象的創建我們javaweb程序員管不著,這個對象的創建只能是Tomcat來說了算,Tomcat只創建了一個,所以導致了單例,但是屬于假單例。真單例模式,構造方法是私有化的。)

      • 第二:無參數構造方法、init方法只在第一次用戶發送請求的時候執行。也就是說無參數構造方法只執行一次。init方法也只被Tomcat服務器調用一次。

      • 第三:只要用戶發送一次請求:service方法必然會被Tomcat服務器調用一次。發送100次請求,service方法會被調用100次。

    • 關閉服務器的時候,控制臺輸出了以下內容:

      AServlet's destroy method execute!
    • 通過以上輸出內容,可以得出以下結論:

      • Servlet的destroy方法只被Tomcat服務器調用一次。

      • destroy方法是在什么時候被調用的?

        • 在服務器關閉的時候。

        • 因為服務器關閉的時候要銷毀AServlet對象的內存。

        • 服務器在銷毀AServlet對象內存之前,Tomcat服務器會自動調用AServlet對象的destroy方法。

    • 請問:destroy方法調用的時候,對象銷毀了還是沒有銷毀呢?

      • destroy方法執行的時候AServlet對象還在,沒有被銷毀。destroy方法執行結束之后,AServlet對象的內存才會被Tomcat釋放。

    • Servlet對象更像一個人的一生:

      • Servlet的無參數構造方法執行:標志著你出生了。

      • Servlet對象的init方法的執行:標志著你正在接受教育。

      • Servlet對象的service方法的執行:標志著你已經開始工作了,已經開始為人類提供服務了。

      • Servlet對象的destroy方法的執行:標志著臨終。有什么遺言,抓緊的。要不然,來不及了。

    • 關于Servlet類中方法的調用次數?

      • 構造方法只執行一次。

      • init方法只執行一次。

      • service方法:用戶發送一次請求則執行一次,發送N次請求則執行N次。

      • destroy方法只執行一次。

    • 當我們Servlet類中編寫一個有參數的構造方法,如果沒有手動編寫無參數構造方法會出現什么問題?

      • 報錯了:500錯誤。

      • 注意:500是一個HTTP協議的錯誤狀態碼。

      • 500一般情況下是因為服務器端的Java程序出現了異常。(服務器端的錯誤都是500錯誤:服務器內部錯誤。)

      • 如果沒有無參數的構造方法,會導致出現500錯誤,無法實例化Servlet對象。

      • 所以,一定要注意:在Servlet開發當中,不建議程序員來定義構造方法,因為定義不當,一不小心就會導致無法實例化Servlet對象。

    • 思考:Servlet的無參數構造方法是在對象第一次創建的時候執行,并且只執行一次。init方法也是在對象第一次創建的時候執行,并且只執行一次。那么這個無參數構造方法可以代替掉init方法嗎?

      • 不能。

      • Servlet規范中有要求,作為javaweb程序員,編寫Servlet類的時候,不建議手動編寫構造方法,因為編寫構造方法,很容易讓無參數構造方法消失,這個操作可能會導致Servlet對象無法實例化。所以init方法是有存在的必要的。

    • init、service、destroy方法中使用最多的是哪個方法?

      • 使用最多就是service方法,service方法是一定要實現的,因為service方法是處理用戶請求的核心方法。

      • 什么時候使用init方法呢?

        • init方法很少用。

        • 通常在init方法當中做初始化操作,并且這個初始化操作只需要執行一次。例如:初始化數據庫連接池,初始化線程池....

      • 什么時候使用destroy方法呢?

        • destroy方法也很少用。

        • 通常在destroy方法當中,進行資源的關閉。馬上對象要被銷毀了,還有什么沒有關閉的,抓緊時間關閉資源。還有什么資源沒保存的,抓緊時間保存一下。

GenericServlet

  • 我們編寫一個Servlet類直接實現Servlet接口有什么缺點?

    • 我們只需要service方法,其他方法大部分情況下是不需要使用的。代碼很丑陋。

  • 適配器設計模式Adapter

    • 手機直接插到220V的電壓上,手機直接就報廢了。怎么辦?可以找一個充電器。這個充電器就是一個適配器。手機連接適配器。適配器連接220V的電壓。這樣問題就解決了。

  • 編寫一個GenericServlet類,這個類是一個抽象類,其中有一個抽象方法service。

    • GenericServlet實現Servlet接口。

    • GenericServlet是一個適配器。

    • 以后編寫的所有Servlet類繼承GenericServlet,重寫service方法即可。

  • 思考:GenericServlet類是否需要改造一下?怎么改造?更利于子類程序的編寫?

    • 思考第一個問題:我提供了一個GenericServlet之后,init方法還會執行嗎?

      • 還會執行。會執行GenericServlet類中的init方法。

    • 思考第二個問題:init方法是誰調用的?

      • Tomcat服務器調用的。

    • 思考第三個問題:init方法中的ServletConfig對象是誰創建的?是誰傳過來的?

      • 都是Tomcat干的。

      • Tomcat服務器先創建了ServletConfig對象,然后調用init方法,將ServletConfig對象傳給了init方法。

    • 思考一下Tomcat服務器偽代碼:

      • public class Tomcat {public static void main(String[] args){// .....// Tomcat服務器偽代碼// 創建LoginServlet對象(通過反射機制,調用無參數構造方法來實例化LoginServlet對象)Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");Object obj = clazz.newInstance();// 向下轉型Servlet servlet = (Servlet)obj;// 創建ServletConfig對象// Tomcat服務器負責將ServletConfig對象實例化出來。// 多態(Tomcat服務器完全實現了Servlet規范)ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();// 調用Servlet的init方法servlet.init(servletConfig);// 調用Servlet的service方法// ....}
        }

ServletConfig

  • 什么是ServletConfig?

    • Servlet對象的配置信息對象。

    • ServletConfig對象中封裝了<servlet></servlet>標簽中的配置信息。(web.xml文件中servlet的配置信息)

  • 一個Servlet對應一個ServletConfig對象。

  • Servlet對象是Tomcat服務器創建,并且ServletConfig對象也是Tomcat服務器創建。并且默認情況下,他們都是在用戶發送第一次請求的時候創建。

  • Tomcat服務器調用Servlet對象的init方法的時候需要傳一個ServletConfig對象的參數給init方法。

  • ServletConfig接口的實現類是Tomcat服務器給實現的。(Tomcat服務器說的就是WEB服務器。)

  • ServletConfig接口有哪些常用的方法?

    • public String getInitParameter(String name); // 通過初始化參數的name獲取value
      public Enumeration<String> getInitParameterNames(); // 獲取所有的初始化參數的name
      public ServletContext getServletContext(); // 獲取ServletContext對象
      public String getServletName(); // 獲取Servlet的name
    • 以上方法在Servlet類當中,都可以使用this去調用。因為GenericServlet實現了ServletConfig接口。

ServletContext

  • 一個Servlet對象對應一個ServletConfig。100個Servlet對象則對應100個ServletConfig對象。

  • 只要在同一個webapp當中,只要在同一個應用當中,所有的Servlet對象都是共享同一個ServletContext對象的。

  • ServletContext對象在服務器啟動階段創建,在服務器關閉的時候銷毀。這就是ServletContext對象的生命周期。ServletContext對象是應用級對象。

  • Tomcat服務器中有一個webapps,這個webapps下可以存放webapp,可以存放多個webapp,假設有100個webapp,那么就有100個ServletContext對象。但是,總之,一個應用,一個webapp肯定是只有一個ServletContext對象。

  • ServletContext被稱為Servlet上下文對象。(Servlet對象的四周環境對象。)

  • 一個ServletContext對象通常對應的是一個web.xml文件。

  • ServletContext對應顯示生活中的什么例子呢?

    • 一個教室里有多個學生,那么每一個學生就是一個Servlet,這些學生都在同一個教室當中,那么我們可以把這個教室叫做ServletContext對象。那么也就是說放在這個ServletContext對象(環境)當中的數據,在同一個教室當中,物品都是共享的。比如:教室中有一個空調,所有的學生都可以操作。可見,空調是共享的。因為空調放在教室當中。教室就是ServletContext對象。

  • ServletContext是一個接口,Tomcat服務器對ServletContext接口進行了實現。

    • ServletContext對象的創建也是Tomcat服務器來完成的。啟動webapp的時候創建的。

  • ServletContext接口中有哪些常用的方法?

    • public String getInitParameter(String name); // 通過初始化參數的name獲取value
      public Enumeration<String> getInitParameterNames(); // 獲取所有的初始化參數的name
    • <!--以上兩個方法是ServletContext對象的方法,這個方法獲取的是什么信息?是以下的配置信息-->
      <context-param><param-name>pageSize</param-name><param-value>10</param-value>
      </context-param>
      <context-param><param-name>startIndex</param-name><param-value>0</param-value>
      </context-param>
      <!--注意:以上的配置信息屬于應用級的配置信息,一般一個項目中共享的配置信息會放到以上的標簽當中。-->
      <!--如果你的配置信息只是想給某一個servlet作為參考,那么你配置到servlet標簽當中即可,使用ServletConfig對象來獲取。-->
    • // 獲取應用的根路徑(非常重要),因為在java源代碼當中有一些地方可能會需要應用的根路徑,這個方法可以動態獲取應用的根路徑
      // 在java源碼當中,不要將應用的根路徑寫死,因為你永遠都不知道這個應用在最終部署的時候,起一個什么名字。
      public String getContextPath();
      //String contextPath = application.getContextPath();
    • // 獲取文件的絕對路徑(真實路徑)
      public String getRealPath(String path);
    • // 通過ServletContext對象也是可以記錄日志的
      public void log(String message);
      public void log(String message, Throwable t);
      // 這些日志信息記錄到哪里了?
      // localhost.2021-11-05.log
      ?
      // Tomcat服務器的logs目錄下都有哪些日志文件?
      //catalina.2021-11-05.log 服務器端的java程序運行的控制臺信息。
      //localhost.2021-11-05.log ServletContext對象的log方法記錄的日志信息存儲到這個文件中。
      //localhost_access_log.2021-11-05.txt 訪問日志
    • // ServletContext對象還有另一個名字:應用域(后面還有其他域,例如:請求域、會話域)
      ?
      // 如果所有的用戶共享一份數據,并且這個數據很少的被修改,并且這個數據量很少,可以將這些數據放到ServletContext這個應用域中
      ?
      // 為什么是所有用戶共享的數據? 不是共享的沒有意義。因為ServletContext這個對象只有一個。只有共享的數據放進去才有意義。
      ?
      // 為什么數據量要小? 因為數據量比較大的話,太占用堆內存,并且這個對象的生命周期比較長,服務器關閉的時候,這個對象才會被銷毀。大數據量會影響服務器的性能。占用內存較小的數據量可以考慮放進去。
      ?
      // 為什么這些共享數據很少的修改,或者說幾乎不修改?
      // 所有用戶共享的數據,如果涉及到修改操作,必然會存在線程并發所帶來的安全問題。所以放在ServletContext對象中的數據一般都是只讀的。
      ?
      // 數據量小、所有用戶共享、又不修改,這樣的數據放到ServletContext這個應用域當中,會大大提升效率。因為應用域相當于一個緩存,放到緩存中的數據,下次在用的時候,不需要從數據庫中再次獲取,大大提升執行效率。
      ?
      // 存(怎么向ServletContext應用域中存數據)
      public void setAttribute(String name, Object value); // map.put(k, v)
      // 取(怎么從ServletContext應用域中取數據)
      public Object getAttribute(String name); // Object v = map.get(k)
      // 刪(怎么刪除ServletContext應用域中的數據)
      public void removeAttribute(String name); // map.remove(k)
      ?
      ?
  • 注意:以后我們編寫Servlet類的時候,實際上是不會去直接繼承GenericServlet類的,因為我們是B/S結構的系統,這種系統是基于HTTP超文本傳輸協議的,在Servlet規范當中,提供了一個類叫做HttpServlet,它是專門為HTTP協議準備的一個Servlet類。我們編寫的Servlet類要繼承HttpServlet。(HttpServlet是HTTP協議專用的。)使用HttpServlet處理HTTP協議更便捷。但是你需要直到它的繼承結構:

    • jakarta.servlet.Servlet(接口)【爺爺】
      jakarta.servlet.GenericServlet implements Servlet(抽象類)【兒子】
      jakarta.servlet.http.HttpServlet extends GenericServlet(抽象類)【孫子】我們以后編寫的Servlet要繼承HttpServlet類。
      
  • 大家到目前為止都接觸過哪些緩存機制了?

    • 堆內存當中的字符串常量池。

      • "abc" 先在字符串常量池中查找,如果有,直接拿來用。如果沒有則新建,然后再放入字符串常量池。

    • 堆內存當中的整數型常量池。

      • [-128 ~ 127] 一共256個Integer類型的引用,放在整數型常量池中。沒有超出這個范圍的話,直接從常量池中取。

    • 連接池(Connection Cache)

      • 這里所說的連接池中的連接是java語言連接數據庫的連接對象:java.sql.Connection對象。

      • JVM是一個進程。MySQL數據庫是一個進程。進程和進程之間建立連接,打開通道是很費勁的。是很耗費資源的。怎么辦?可以提前先創建好N個Connection連接對象,將連接對象放到一個集合當中,我們把這個放有Connection對象的集合稱為連接池。每一次用戶連接的時候不需要再新建連接對象,省去了新建的環節,直接從連接池中獲取連接對象,大大提升訪問效率。

      • 連接池

        • 最小連接數

        • 最大連接數

        • 連接池可以提高用戶的訪問效率。當然也可以保證數據庫的安全性。

    • 線程池

      • Tomcat服務器本身就是支持多線程的。

      • Tomcat服務器是在用戶發送一次請求,就新建一個Thread線程對象嗎?

        • 當然不是,實際上是在Tomcat服務器啟動的時候,會先創建好N多個線程Thread對象,然后將線程對象放到集合當中,稱為線程池。用戶發送請求過來之后,需要有一個對應的線程來處理這個請求,這個時候線程對象就會直接從線程池中拿,效率比較高。

        • 所有的WEB服務器,或者應用服務器,都是支持多線程的,都有線程池機制。

    • redis

      • NoSQL數據庫。非關系型數據庫。緩存數據庫。

    • 向ServletContext應用域中存儲數據,也等于是將數據存放到緩存cache當中了。

HTTP協議

  • 什么是協議?

    • 協議實際上是某些人,或者某些組織提前制定好的一套規范,大家都按照這個規范來,這樣可以做到溝通無障礙。

    • 協議就是一套規范,就是一套標準。由其他人或其他組織來負責制定的。

    • 我說的話你能聽懂,你說的話,我也能聽懂,這說明我們之間是有一套規范的,一套協議的,這套協議就是:中國普通話協議。我們都遵守這套協議,我們之間就可以溝通無障礙。

  • 什么是HTTP協議?

    • HTTP協議:是W3C制定的一種超文本傳輸協議。(通信協議:發送消息的模板提前被制定好。)

    • W3C:

      • 萬維網聯盟組織

      • 負責制定標準的:HTTP HTML4.0 HTML5 XML DOM等規范都是W3C制定的。

      • 萬維網之父:蒂姆·伯納斯·李

    • 什么是超文本?

      • 超文本說的就是:不是普通文本,比如流媒體:聲音、視頻、圖片等。

      • HTTP協議支持:不但可以傳送普通字符串,同樣支持傳遞聲音、視頻、圖片等流媒體信息。

    • 這種協議游走在B和S之間。B向S發數據要遵循HTTP協議。S向B發數據同樣需要遵循HTTP協議。這樣B和S才能解耦合。

    • 什么是解耦合?

      • B不依賴S。

      • S也不依賴B。

    • B/S表示:B/S結構的系統(瀏覽器訪問WEB服務器的系統)

    • 瀏覽器 向 WEB服務器發送數據,叫做:請求(request)

    • WEB服務器 向 瀏覽器發送數據,叫做:響應(response)

    • HTTP協議包括:

      • 請求協議

        • 瀏覽器 向 WEB服務器發送數據的時候,這個發送的數據需要遵循一套標準,這套標準中規定了發送的數據具體格式。

      • 響應協議

        • WEB服務器 向 瀏覽器發送數據的時候,這個發送的數據需要遵循一套標準,這套標準中規定了發送的數據具體格式。

    • HTTP協議就是提前制定好的一種消息模板。

      • 不管你是哪個品牌的瀏覽器,都是這么發。

      • 不管你是哪個品牌的WEB服務器,都是這么發。

      • FF瀏覽器 可以向 Tomcat發送請求,也可以向Jetty服務器發送請求。瀏覽器不依賴具體的服務器品牌。

      • WEB服務器也不依賴具體的瀏覽器品牌。可以是FF瀏覽器,也可以是Chrome瀏覽器,可以是IE,都行。

  • HTTP的請求協議(B --> S)

    • HTTP的請求協議包括:4部分

      • 請求行

      • 請求頭

      • 空白行

      • 請求體

    • HTTP請求協議的具體報文:GET請求

      • GET /servlet05/getServlet?username=lucy&userpwd=1111 HTTP/1.1                           請求行
        Host: localhost:8080                                                                    請求頭
        Connection: keep-alive
        sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
        sec-ch-ua-mobile: ?0
        sec-ch-ua-platform: "Windows"
        Upgrade-Insecure-Requests: 1
        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
        Sec-Fetch-Site: same-origin
        Sec-Fetch-Mode: navigate
        Sec-Fetch-User: ?1
        Sec-Fetch-Dest: document
        Referer: http://localhost:8080/servlet05/index.html
        Accept-Encoding: gzip, deflate, br
        Accept-Language: zh-CN,zh;q=0.9空白行請求體
        

    • HTTP請求協議的具體報文:POST請求

      • POST /servlet05/postServlet HTTP/1.1                                                  請求行
        Host: localhost:8080                                                                  請求頭
        Connection: keep-alive
        Content-Length: 25
        Cache-Control: max-age=0
        sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
        sec-ch-ua-mobile: ?0
        sec-ch-ua-platform: "Windows"
        Upgrade-Insecure-Requests: 1
        Origin: http://localhost:8080
        Content-Type: application/x-www-form-urlencoded
        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
        Sec-Fetch-Site: same-origin
        Sec-Fetch-Mode: navigate
        Sec-Fetch-User: ?1
        Sec-Fetch-Dest: document
        Referer: http://localhost:8080/servlet05/index.html
        Accept-Encoding: gzip, deflate, br
        Accept-Language: zh-CN,zh;q=0.9空白行
        username=lisi&userpwd=123                                                             請求體
        
    • 請求行

      • 包括三部分:

        • 第一部分:請求方式(7種)

          • get(常用的)

          • post(常用的)

          • delete

          • put

          • head

          • options

          • trace

        • 第二部分:URI

          • 什么是URI? 統一資源標識符。代表網絡中某個資源的名字。但是通過URI是無法定位資源的。

          • 什么是URL?統一資源定位符。代表網絡中某個資源,同時,通過URL是可以定位到該資源的。

          • URI和URL什么關系,有什么區別?

            • URL包括URI

            • http://localhost:8080/servlet05/index.html 這是URL。

            • /servlet05/index.html 這是URI。

        • 第三部分:HTTP協議版本號

    • 請求頭

      • 請求的主機

      • 主機的端口

      • 瀏覽器信息

      • 平臺信息

      • cookie等信息

      • ....

    • 空白行

      • 空白行是用來區分“請求頭”和“請求體”

    • 請求體

      • 向服務器發送的具體數據。

  • HTTP的響應協議(S --> B)

    • HTTP的響應協議包括:4部分

      • 狀態行

      • 響應頭

      • 空白行

      • 響應體

    • 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方法就會執行一次。@Overridepublic void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest  request;HttpServletResponse response;try {// 將ServletRequest和ServletResponse向下轉型為帶有Http的HttpServletRequest和HttpServletResponserequest = (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 TRACEString 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 logicdoGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {// Invalid date header - proceed as if none was setifModifiedSince = -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 lessmaybeSetLastModified(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表單中指定請求路徑即可。

關于一個web站點的歡迎頁面

  • 什么是一個web站點的歡迎頁面?

    • 對于一個webapp來說,我們是可以設置它的歡迎頁面的。

    • 設置了歡迎頁面之后,當你訪問這個webapp的時候,或者訪問這個web站點的時候,沒有指定任何“資源路徑”,這個時候會默認訪問你的歡迎頁面。

    • 我們一般的訪問方式是:

      • http://localhost:8080/servlet06/login.html 這種方式是指定了要訪問的就是login.html資源。

    • 如果我們訪問的方式是:

      • http://localhost:8080/servlet06 如果我們訪問的就是這個站點,沒有指定具體的資源路徑。它默認會訪問誰呢?

      • 默認會訪問你設置的歡迎頁面。

  • 怎么設置歡迎頁面呢?

    • 第一步:我在IDEA工具的web目錄下新建了一個文件login.html

    • 第二步:在web.xml文件中進行了以下的配置

      • <welcome-file-list><welcome-file>login.html</welcome-file></welcome-file-list>
        
      • 注意:設置歡迎頁面的時候,這個路徑不需要以“/”開始。并且這個路徑默認是從webapp的根下開始查找。

    • 第三步:啟動服務器,瀏覽器地址欄輸入地址

      • http://localhost:8080/servlet07

  • 如果在webapp的根下新建一個目錄,目錄中再給一個文件,那么這個歡迎頁該如何設置呢?

    • 在webapp根下新建page1

    • 在page1下新建page2目錄

    • 在page2目錄下新建page.html頁面

    • 在web.xml文件中應該這樣配置

      • <welcome-file-list><welcome-file>page1/page2/page.html</welcome-file>
        </welcome-file-list>
        
      • 注意:路徑不需要以“/”開始,并且路徑默認從webapp的根下開始找。

  • 一個webapp是可以設置多個歡迎頁面的

    • <welcome-file-list><welcome-file>page1/page2/page.html</welcome-file><welcome-file>login.html</welcome-file>
      </welcome-file-list>
      
    • 注意:越靠上的優先級越高。找不到的繼續向下找。

  • 你有沒有注意一件事:當我的文件名設置為index.html的時候,不需要在web.xml文件中進行配置歡迎頁面。這是為什么?

    • 這是因為小貓咪Tomcat服務器已經提前配置好了。

    • 實際上配置歡迎頁面有兩個地方可以配置:

      • 一個是在webapp內部的web.xml文件中。(在這個地方配置的屬于局部配置)

      • 一個是在CATALINA_HOME/conf/web.xml文件中進行配置。(在這個地方配置的屬于全局配置)

        • <welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file>
          </welcome-file-list>
          
        • Tomcat服務器的全局歡迎頁面是:index.html index.htm index.jsp。如果你一個web站點沒有設置局部的歡迎頁面,Tomcat服務器就會以index.html index.htm index.jsp作為一個web站點的歡迎頁面。

      • 注意原則:局部優先原則。(就近原則)

  • 歡迎頁可以是一個Servlet嗎?

    • 當然可以。

    • 你不要多想,歡迎頁就是一個資源,既然是一個資源,那么可以是靜態資源,也可以是動態資源。

    • 靜態資源:index.html welcome.html .....

    • 動態資源:Servlet類。

    • 步驟:

      • 第一步:寫一個Servlet

        • public class WelcomeServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html");PrintWriter out = response.getWriter();out.print("<h1>welcome to bjpowernode!</h1>");}
          }
          

      • 第二步:在web.xml文件中配置servlet

        •     <servlet><servlet-name>welcomeServlet</servlet-name><servlet-class>com.bjpowernode.javaweb.servlet.WelcomeServlet</servlet-class></servlet><servlet-mapping><servlet-name>welcomeServlet</servlet-name><url-pattern>/fdsa/fds/a/fds/af/ds/af/dsafdsafdsa</url-pattern></servlet-mapping>
          
      • 第三步:在web.xml文件中配置歡迎頁

        •     <welcome-file-list><welcome-file>fdsa/fds/a/fds/af/ds/af/dsafdsafdsa</welcome-file></welcome-file-list>
          

關于WEB-INF目錄

  • 在WEB-INF目錄下新建了一個文件:welcome.html

  • 打開瀏覽器訪問:http://localhost:8080/servlet07/WEB-INF/welcome.html 出現了404錯誤。

  • 注意:放在WEB-INF目錄下的資源是受保護的。在瀏覽器上不能夠通過路徑直接訪問。所以像HTML、CSS、JS、image等靜態資源一定要放到WEB-INF目錄之外。

HttpServletRequest接口詳解

  • HttpServletRequest是一個接口,全限定名稱:jakarta.servlet.http.HttpServletRequest

  • HttpServletRequest接口是Servlet規范中的一員。

  • HttpServletRequest接口的父接口:ServletRequest

    • public interface HttpServletRequest extends ServletRequest {}
      
  • HttpServletRequest接口的實現類誰寫的? HttpServletRequest對象是誰給創建的?

    • 通過測試:org.apache.catalina.connector.RequestFacade 實現了 HttpServletRequest接口

      • public class RequestFacade implements HttpServletRequest {}
        
    • 測試結果說明:Tomcat服務器(WEB服務器、WEB容器)實現了HttpServletRequest接口,還是說明了Tomcat服務器實現了Servlet規范。而對于我們javaweb程序員來說,實際上不需要關心這個,我們只需要面向接口編程即可。我們關心的是HttpServletRequest接口中有哪些方法,這些方法可以完成什么功能!!!!

  • HttpServletRequest對象中都有什么信息?都包裝了什么信息?

    • HttpServletRequest對象是Tomcat服務器負責創建的。這個對象中封裝了什么信息?封裝了HTTP的請求協議。

    • 實際上是用戶發送請求的時候,遵循了HTTP協議,發送的是HTTP的請求協議,Tomcat服務器將HTTP協議中的信息以及數據全部解析出來,然后Tomcat服務器把這些信息封裝到HttpServletRequest對象當中,傳給了我們javaweb程序員。

    • javaweb程序員面向HttpServletRequest接口編程,調用方法就可以獲取到請求的信息了。

  • request和response對象的生命周期?

    • request對象和response對象,一個是請求對象,一個是響應對象。這兩個對象只在當前請求中有效。

    • 一次請求對應一個request。

    • 兩次請求則對應兩個request。

    • .....

  • HttpServletRequest接口中有哪些常用的方法?

    • 怎么獲取前端瀏覽器用戶提交的數據?

      • Map<String,String[]> getParameterMap() 這個是獲取Map
        Enumeration<String> getParameterNames() 這個是獲取Map集合中所有的key
        String[] getParameterValues(String name) 根據key獲取Map集合的value
        String getParameter(String name)  獲取value這個一維數組當中的第一個元素。這個方法最常用。
        // 以上的4個方法,和獲取用戶提交的數據有關系。
        
      • 思考:如果是你,前端的form表單提交了數據之后,你準備怎么存儲這些數據,你準備采用什么樣的數據結構去存儲這些數據呢?

        • 前端提交的數據格式:username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt

        • 我會采用Map集合來存儲:

          • Map<String,String>key存儲Stringvalue存儲String這種想法對嗎?不對。如果采用以上的數據結構存儲會發現key重復的時候value覆蓋。key         value---------------------username    abcuserpwd     111aihao       saihao       daihao       tt這樣是不行的,因為map的key不能重復。
            Map<String, String[]>key存儲Stringvalue存儲String[]key				value-------------------------------username		{"abc"}userpwd			{"111"}aihao			{"s","d","tt"}
            
        • 注意:前端表單提交數據的時候,假設提交了120這樣的“數字”,其實是以字符串"120"的方式提交的,所以服務器端獲取到的一定是一個字符串的"120",而不是一個數字。(前端永遠提交的是字符串,后端獲取的也永遠是字符串。)

    • 手工開發一個webapp。測試HttpServletRequest接口中的相關方法。

      • 先測試了4個常用的方法,獲取請求參數的四個方法。

        • Map<String,String[]> parameterMap = request.getParameterMap();Enumeration<String> names = request.getParameterNames();String[] values = request.getParameterValues("name");String value = request.getParameter("name");
          

      • request對象實際上又稱為“請求域”對象。

        • 應用域對象是什么?

          • ServletContext (Servlet上下文對象。)

          • 什么情況下會考慮向ServletContext這個應用域當中綁定數據呢?

            • 第一:所有用戶共享的數據。

            • 第二:這個共享的數據量很小。

            • 第三:這個共享的數據很少的修改操作。

            • 在以上三個條件都滿足的情況下,使用這個應用域對象,可以大大提高我們程序執行效率。

            • 實際上向應用域當中綁定數據,就相當于把數據放到了緩存(Cache)當中,然后用戶訪問的時候直接從緩存中取,減少IO的操作,大大提升系統的性能,所以緩存技術是提高系統性能的重要手段。

          • 你見過哪些緩存技術呢?

            • 字符串常量池

            • 整數型常量池 [-128~127],但凡是在這個范圍當中的Integer對象不再創建新對象,直接從這個整數型常量池中獲取。大大提升系統性能。

            • 數據庫連接池(提前創建好N個連接對象,將連接對象放到集合當中,使用連接對象的時候,直接從緩存中拿。省去了連接對象的創建過程。效率提升。)

            • 線程池(Tomcat服務器就是支持多線程的。所謂的線程池就是提前先創建好N個線程對象,將線程對象存儲到集合中,然后用戶請求過來之后,直接從線程池中獲取線程對象,直接拿來用。提升系統性能)

            • 后期你還會學習更多的緩存技術,例如:redis、mongoDB.....

          • ServletContext當中有三個操作域的方法:

            • void setAttribute(String name, Object obj); // 向域當中綁定數據。
              Object getAttribute(String name); // 從域當中根據name獲取數據。
              void removeAttribute(String name); // 將域當中綁定的數據移除// 以上的操作類似于Map集合的操作。
              Map<String, Object> map;
              map.put("name", obj); // 向map集合中放key和value
              Object obj = map.get("name"); // 通過map集合的key獲取value
              map.remove("name"); // 通過Map集合的key刪除key和value這個鍵值對。
              
        • “請求域”對象

          • “請求域”對象要比“應用域”對象范圍小很多。生命周期短很多。請求域只在一次請求內有效。

          • 一個請求對象request對應一個請求域對象。一次請求結束之后,這個請求域就銷毀了。

          • 請求域對象也有這三個方法:

            • void setAttribute(String name, Object obj); // 向域當中綁定數據。
              Object getAttribute(String name); // 從域當中根據name獲取數據。
              void removeAttribute(String name); // 將域當中綁定的數據移除
              
          • 請求域和應用域的選用原則?

            • 盡量使用小的域對象,因為小的域對象占用的資源較少。

        • 跳轉

          • 轉發(一次請求)

            • // 第一步:獲取請求轉發器對象
              RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
              // 第二步:調用轉發器的forward方法完成跳轉/轉發
              dispatcher.forward(request,response);// 第一步和第二步代碼可以聯合在一起。
              request.getRequestDispatcher("/b").forward(request,response);
        • 兩個Servlet怎么共享數據?

          • 將數據放到ServletContext應用域當中,當然是可以的,但是應用域范圍太大,占用資源太多。不建議使用。

          • 可以將數據放到request域當中,然后AServlet轉發到BServlet,保證AServlet和BServlet在同一次請求當中,這樣就可以做到兩個Servlet,或者多個Servlet共享同一份數據。

        • 轉發的下一個資源必須是一個Servlet嗎?

          • 不一定,只要是Tomcat服務器當中的合法資源,都是可以轉發的。例如:html....

          • 注意:轉發的時候,路徑的寫法要注意,轉發的路徑以“/”開始,不加項目名。

        • 關于request對象中兩個非常容易混淆的方法:

          • // uri?username=zhangsan&userpwd=123&sex=1
            String username = request.getParameter("username");// 之前一定是執行過:request.setAttribute("name", new Object())
            Object obj = request.getAttribute("name");// 以上兩個方法的區別是什么?
            // 第一個方法:獲取的是用戶在瀏覽器上提交的數據。
            // 第二個方法:獲取的是請求域當中綁定的數據。
            
        • HttpServletRequest接口的其他常用方法:

          • // 獲取客戶端的IP地址
            String remoteAddr = request.getRemoteAddr();// get請求在請求行上提交數據。
            // post請求在請求體中提交數據。
            // 設置請求體的字符集。(顯然這個方法是處理POST請求的亂碼問題。這種方式并不能解決get請求的亂碼問題。)
            // Tomcat10之后,request請求體當中的字符集默認就是UTF-8,不需要設置字符集,不會出現亂碼問題。
            // Tomcat9前(包括9在內),如果前端請求體提交的是中文,后端獲取之后出現亂碼,怎么解決這個亂碼?執行以下代碼。
            request.setCharacterEncoding("UTF-8");// 在Tomcat9之前(包括9),響應中文也是有亂碼的,怎么解決這個響應的亂碼?
            response.setContentType("text/html;charset=UTF-8");
            // 在Tomcat10之后,包括10在內,響應中文的時候就不在出現亂碼問題了。以上代碼就不需要設置UTF-8了。// 注意一個細節
            // 在Tomcat10包括10在內之后的版本,中文將不再出現亂碼。(這也體現了中文地位的提升。)// get請求亂碼問題怎么解決?
            // get請求發送的時候,數據是在請求行上提交的,不是在請求體當中提交的。
            // get請求亂碼怎么解決
            // 方案:修改CATALINA_HOME/conf/server.xml配置文件
            <Connector URIEncoding="UTF-8" />
            // 注意:從Tomcat8之后,URIEncoding的默認值就是UTF-8,所以GET請求也沒有亂碼問題了。// 獲取應用的根路徑
            String contextPath = request.getContextPath();// 獲取請求方式
            String method = request.getMethod();// 獲取請求的URI
            String uri = request.getRequestURI();  // /aaa/testRequest// 獲取servlet path
            String servletPath = request.getServletPath(); //   /testRequest

使用純Servlet做一個單表的CRUD操作

  • 使用純粹的Servlet完成單表【對部門的】的增刪改查操作。(B/S結構的。)

  • 實現步驟

    • 第一步:準備一張數據庫表。(sql腳本)

      • # 部門表
        drop table if exists dept;
        create table dept(deptno int primary key,dname varchar(255),loc varchar(255)
        );
        insert into dept(deptno, dname, loc) values(10, 'XiaoShouBu', 'BEIJING');
        insert into dept(deptno, dname, loc) values(20, 'YanFaBu', 'SHANGHAI');
        insert into dept(deptno, dname, loc) values(30, 'JiShuBu', 'GUANGZHOU');
        insert into dept(deptno, dname, loc) values(40, 'MeiTiBu', 'SHENZHEN');
        commit;
        select * from dept;
        
    • 第二步:準備一套HTML頁面(項目原型)【前端開發工具使用HBuilder】

      • 把HTML頁面準備好

      • 然后將HTML頁面中的鏈接都能夠跑通。(頁面流轉沒問題。)

      • 應該設計哪些頁面呢?

        • 歡迎頁面:index.html

        • 列表頁面:list.html(以列表頁面為核心,展開其他操作。)

        • 新增頁面:add.html

        • 修改頁面:edit.html

        • 詳情頁面:detail.html

    • 第三步:分析我們這個系統包括哪些功能?

      • 什么叫做一個功能呢?

        • 只要 這個操作連接了數據庫,就表示一個獨立的功能。

      • 包括哪些功能?

        • 查看部門列表

        • 新增部門

        • 刪除部門

        • 查看部門詳細信息

        • 跳轉到修改頁面

        • 修改部門

    • 第四步:在IDEA當中搭建開發環境

      • 創建一個webapp(給這個webapp添加servlet-api.jar和jsp-api.jar到classpath當中。)

      • 向webapp中添加連接數據庫的jar包(mysql驅動)

        • 必須在WEB-INF目錄下新建lib目錄,然后將mysql的驅動jar包拷貝到這個lib目錄下。這個目錄名必須叫做lib,全部小寫的。

      • JDBC的工具類

      • 將所有HTML頁面拷貝到web目錄下。

    • 第五步:實現第一個功能:查看部門列表

      • 我們應該怎么去實現一個功能呢?

        • 建議:你可以從后端往前端一步一步寫。也可以從前端一步一步往后端寫。都可以。但是千萬要記住不要想起來什么寫什么。你寫代碼的過程最好是程序的執行過程。也就是說:程序執行到哪里,你就寫哪里。這樣一個順序流下來之后,基本上不會出現什么錯誤、意外。

        • 從哪里開始?

          • 假設從前端開始,那么一定是從用戶點擊按鈕那里開始的。

      • 第一:先修改前端頁面的超鏈接,因為用戶先點擊的就是這個超鏈接。

        • <a href="/oa/dept/list">查看部門列表</a>
          
      • 第二:編寫web.xml文件

        • <servlet><servlet-name>list</servlet-name><servlet-class>com.bjpowernode.oa.web.action.DeptListServlet</servlet-class>
          </servlet>
          <servlet-mapping><servlet-name>list</servlet-name><!--web.xml文件中的這個路徑也是以“/”開始的,但是不需要加項目名--><url-pattern>/dept/list</url-pattern>
          </servlet-mapping>
          
      • 第三:編寫DeptListServlet類繼承HttpServlet類。然后重寫doGet方法。

        • package com.bjpowernode.oa.web.action;import jakarta.servlet.ServletException;
          import jakarta.servlet.http.HttpServlet;
          import jakarta.servlet.http.HttpServletRequest;
          import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class DeptListServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}
          }
          
      • 第四:在DeptListServlet類的doGet方法中連接數據庫,查詢所有的部門,動態的展示部門列表頁面.

        • 分析list.html頁面中哪部分是固定死的,哪部分是需要動態展示的。

        • list.html頁面中的內容所有的雙引號要替換成單引號,因為out.print("")這里有一個雙引號,容易沖突。

        • 現在寫完這個功能之后,你會有一種感覺,感覺開發很繁瑣,只使用servlet寫代碼太繁瑣了。

        • while(rs.next()){String deptno = rs.getString("a");String dname = rs.getString("dname");String loc = rs.getString("loc");out.print("			<tr>");out.print("				<td>"+(++i)+"</td>");out.print("				<td>"+deptno+"</td>");out.print("				<td>"+dname+"</td>");out.print("				<td>");out.print("					<a href=''>刪除</a>");out.print("					<a href='edit.html'>修改</a>");out.print("					<a href='detail.html'>詳情</a>");out.print("				</td>");out.print("			</tr>");
          }
          
    • 第六步:查看部門詳情。

      • 建議:從前端往后端一步一步實現。首先要考慮的是,用戶點擊的是什么?用戶點擊的東西在哪里?

        • 一定要先找到用戶點的“詳情”在哪里。找了半天,終于在后端的java程序中找到了

          • <a href='寫一個路徑'>詳情</a>
            
          • 詳情 是需要連接數據庫的,所以這個超鏈接點擊之后也是需要執行一段java代碼的。所以要將這個超鏈接的路徑修改一下。

          • 注意:修改路徑之后,這個路徑是需要加項目名的。"/oa/dept/detail"

        • 技巧:

          • out.print("<a href='"+contextPath+"/dept/detail?deptno="+deptno+"'>詳情</a>");
            
          • 重點:向服務器提交數據的格式:uri?name=value&name=value&name=value&name=value

          • 這里的問號,必須是英文的問號。不能中文的問號。

      • 解決404的問題。寫web.xml文件。

        • <servlet><servlet-name>detail</servlet-name><servlet-class>com.bjpowernode.oa.web.action.DeptDetailServlet</servlet-class>
          </servlet>
          <servlet-mapping><servlet-name>detail</servlet-name><url-pattern>/dept/detail</url-pattern>
          </servlet-mapping>
          
      • 編寫一個類:DeptDetailServlet繼承HttpServlet,重寫doGet方法。

        • package com.bjpowernode.oa.web.action;import jakarta.servlet.ServletException;
          import jakarta.servlet.http.HttpServlet;
          import jakarta.servlet.http.HttpServletRequest;
          import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class DeptDetailServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//中文思路(思路來源于:你要做什么?目標:查看部門詳細信息。)// 第一步:獲取部門編號// 第二步:根據部門編號查詢數據庫,獲取該部門編號對應的部門信息。// 第三步:將部門信息響應到瀏覽器上。(顯示一個詳情。)}
          }
          
      • 在doGet方法當中:連接數據庫,根據部門編號查詢該部門的信息。動態展示部門詳情頁。

    • 第七步:刪除部門

      • 怎么開始?從哪里開始?從前端頁面開始,用戶點擊刪除按鈕的時候,應該提示用戶是否刪除。因為刪除這個動作是比較危險的。任何系統在進行刪除操作之前,是必須要提示用戶的,因為這個刪除的動作有可能是用戶誤操作。(在前端頁面上寫JS代碼,來提示用戶是否刪除。)

        • <a href="javascript:void(0)" οnclick="del(30)" >刪除</a>
          <script type="text/javascript">function del(dno){if(window.confirm("親,刪了不可恢復哦!")){document.location.href = "/oa/dept/delete?deptno=" + dno;}}
          </script>
          
      • 以上的前端程序要寫到后端的java代碼當中:

        • DeptListServlet類的doGet方法當中,使用out.print()方法,將以上的前端代碼輸出到瀏覽器上。

      • 解決404的問題:

        • http://localhost:8080/oa/dept/delete?deptno=30

        • web.xml文件

          • <servlet><servlet-name>delete</servlet-name><servlet-class>com.bjpowernode.oa.web.action.DeptDelServlet</servlet-class>
            </servlet>
            <servlet-mapping><servlet-name>delete</servlet-name><url-pattern>/dept/delete</url-pattern>
            </servlet-mapping>
            
        • 編寫DeptDelServlet繼承HttpServlet,重寫doGet方法。

        • package com.bjpowernode.oa.web.action;import jakarta.servlet.ServletException;
          import jakarta.servlet.http.HttpServlet;
          import jakarta.servlet.http.HttpServletRequest;
          import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class DeptDelServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 根據部門編號,刪除部門。}
          }
          
        • 刪除成功或者失敗的時候的一個處理(這里我們選擇了轉發,并沒有使用重定向機制。)

          • // 判斷刪除成功了還是失敗了。
            if (count == 1) {//刪除成功//仍然跳轉到部門列表頁面//部門列表頁面的顯示需要執行另一個Servlet。怎么辦?轉發。request.getRequestDispatcher("/dept/list").forward(request, response);
            }else{// 刪除失敗request.getRequestDispatcher("/error.html").forward(request, response);
            }
            
    • 第八步:新增部門

      • 注意:最后保存成功之后,轉發到 /dept/list 的時候,會出現405,為什么?

        • 第一:保存用的是post請求。底層要執行doPost方法。

        • 第二:轉發是一次請求,之前是post,之后還是post,因為它是一次請求。

        • 第三:/dept/list Servlet當中只有一個doGet方法。

        • 怎么解決?兩種方案

          • 第一種:在/dept/list Servlet中添加doPost方法,然后在doPost方法中調用doGet。

          • 第二種:重定向。

    • 第九步:跳轉到修改部門的頁面

    • 第十步:修改部門

在一個web應用中應該如何完成資源的跳轉

  • 在一個web應用中通過兩種方式,可以完成資源的跳轉:

    • 第一種方式:轉發

    • 第二種方式:重定向

  • 轉發和重定向有什么區別?

    • 代碼上有什么區別?

      • 轉發

        • // 獲取請求轉發器對象
          RequestDispatcher dispatcher = request.getRequestDispatcher("/dept/list");
          // 調用請求轉發器對象的forward方法完成轉發
          dispatcher.forward(request, response);// 合并一行代碼
          request.getRequestDispatcher("/dept/list").forward(request, response);
          // 轉發的時候是一次請求,不管你轉發了多少次。都是一次請求。
          // AServlet轉發到BServlet,再轉發到CServlet,再轉發到DServlet,不管轉發了多少次,都在同一個request當中。
          // 這是因為調用forward方法的時候,會將當前的request和response對象傳遞給下一個Servlet。
          
      • 重定向

        • // 注意:路徑上要加一個項目名。為什么?
          // 瀏覽器發送請求,請求路徑上是需要添加項目名的。
          // 以下這一行代碼會將請求路徑“/oa/dept/list”發送給瀏覽器
          // 瀏覽器會自發的向服務器發送一次全新的請求:/oa/dept/list
          response.sendRedirect("/oa/dept/list");
          

    • 形式上有什么區別?

      • 轉發(一次請求)

        • 在瀏覽器地址欄上發送的請求是:http://localhost:8080/servlet10/a ,最終請求結束之后,瀏覽器地址欄上的地址還是這個。沒變。

      • 重定向(兩次請求)

        • 在瀏覽器地址欄上發送的請求是:http://localhost:8080/servlet10/a ,最終在瀏覽器地址欄上顯示的地址是:http://localhost:8080/servlet10/b

    • 轉發和重定向的本質區別?

      • 轉發:是由WEB服務器來控制的。A資源跳轉到B資源,這個跳轉動作是Tomcat服務器內部完成的。

      • 重定向:是瀏覽器完成的。具體跳轉到哪個資源,是瀏覽器說了算。

    • 使用一個例子去描述這個轉發和重定向

      • 借錢(轉發:發送了一次請求)

        • 杜老師沒錢了,找張三借錢,其實張三沒有錢,但是張三夠義氣,張三自己找李四借了錢,然后張三把這個錢給了杜老師,杜老師不知道這個錢是李四的,杜老師只求了一個人。杜老師以為這個錢就是張三的。

      • 借錢(重定向:發送了兩次請求)

        • 杜老師沒錢了,找張三借錢,張三沒有錢,張三有一個好哥們,叫李四,李四是個富二代,于是張三將李四的家庭住址告訴了杜老師,杜老師按照這個地址去找到李四,然后從李四那里借了錢。顯然杜老師在這個過程中,求了兩個人。并且杜老師知道最終這個錢是李四借給俺的。

  • 轉發和重定向應該如何選擇?什么時候使用轉發,什么時候使用重定向?

    • 如果在上一個Servlet當中向request域當中綁定了數據,希望從下一個Servlet當中把request域里面的數據取出來,使用轉發機制。

    • 剩下所有的請求均使用重定向。(重定向使用較多。)

  • 跳轉的下一個資源有沒有要求呢?必須是一個Servlet嗎?

    • 不一定,跳轉的資源只要是服務器內部合法的資源即可。包括:Servlet、JSP、HTML.....

  • 轉發會存在瀏覽器的刷新問題。

將oa項目中的資源跳轉修改為合適的跳轉方式

  • 刪除之后,重定向

  • 修改之后,重定向

  • 保存之后,重定向

  • 重定向:

    • 成功

    • 失敗

Servlet注解,簡化配置

  • 分析oa項目中的web.xml文件

    • 現在只是一個單標的CRUD,沒有復雜的業務邏輯,很簡單的一丟丟功能。web.xml文件中就有如此多的配置信息。如果采用這種方式,對于一個大的項目來說,這樣的話web.xml文件會非常龐大,有可能最終會達到幾十兆。

    • 在web.xml文件中進行servlet信息的配置,顯然開發效率比較低,每一個都需要配置一下。

    • 而且在web.xml文件中的配置是很少被修改的,所以這種配置信息能不能直接寫到java類當中呢?可以的。

  • Servlet3.0版本之后,推出了各種Servlet基于注解式開發。優點是什么?

    • 開發效率高,不需要編寫大量的配置信息。直接在java類上使用注解進行標注。

    • web.xml文件體積變小了。

  • 并不是說注解有了之后,web.xml文件就不需要了:

    • 有一些需要變化的信息,還是要配置到web.xml文件中。一般都是 注解+配置文件 的開發模式。

    • 一些不會經常變化修改的配置建議使用注解。一些可能會被修改的建議寫到配置文件中。

  • 我們的第一個注解:

    • jakarta.servlet.annotation.WebServlet
      
    • 在Servlet類上使用:@WebServlet,WebServlet注解中有哪些屬性呢?

      • name屬性:用來指定Servlet的名字。等同于:<servlet-name>

      • urlPatterns屬性:用來指定Servlet的映射路徑。可以指定多個字符串。<url-pattern>

      • loadOnStartUp屬性:用來指定在服務器啟動階段是否加載該Servlet。等同于:<load-on-startup>

      • value屬性:當注解的屬性名是value的時候,使用注解的時候,value屬性名是可以省略的。

      • 注意:不是必須將所有屬性都寫上,只需要提供需要的。(需要什么用什么。)

      • 注意:屬性是一個數組,如果數組中只有一個元素,使用該注解的時候,屬性值的大括號可以省略。

  • 注解對象的使用格式:

    • @注解名稱(屬性名=屬性值, 屬性名=屬性值, 屬性名=屬性值....)

使用模板方法設計模式優化oa項目

  • 上面的注解解決了配置文件的問題。但是現在的oa項目仍然存在一個比較臃腫的問題。

    • 一個單標的CRUD,就寫了6個Servlet。如果一個復雜的業務系統,這種開發方式,顯然會導致類爆炸。(類的數量太大。)

    • 怎么解決這個類爆炸問題?可以使用模板方法設計模式。

  • 怎么解決類爆炸問題?

    • 以前的設計是一個請求一個Servlet類。1000個請求對應1000個Servlet類。導致類爆炸。

    • 可以這樣做:一個請求對應一個方法。一個業務對應一個Servlet類。

    • 處理部門相關業務的對應一個DeptServlet。處理用戶相關業務的對應一個UserServlet。處理銀行卡卡片業務對應一個CardServlet。

分析使用純粹Servlet開發web應用的缺陷

  • 在Servlet當中編寫HTML/CSS/JavaScript等前端代碼。存在什么問題?

    • java程序中編寫前端代碼,編寫難度大。麻煩。

    • java程序中編寫前端代碼,顯然程序的耦合度非常高。

    • java程序中編寫前端代碼,代碼非常不美觀。

    • java程序中編寫前端代碼,維護成本太高。(非常難于維護)

      • 修改小小的一個前端代碼,只要有改動,就需要重新編譯java代碼,生成新的class文件,打一個新的war包,重新發布。

  • 思考一下,如果是你的話,你準備怎么解決這個問題?

    • 思路很重要。使用什么樣的思路去做、去解決這個問題

      • 上面的那個Servlet(Java程序)能不能不寫了,讓機器自動生成。我們程序員只需要寫這個Servlet程序中的“前端的那段代碼”,然后讓機器將我們寫的“前端代碼”自動翻譯生成“Servlet這種java程序”。然后機器再自動將“java”程序編譯生成"class"文件。然后再使用JVM調用這個class中的方法。

關于B/S結構系統的會話機制(session機制)

  • 什么是會話?

    • 會話對應的英語單詞:session

    • 用戶打開瀏覽器,進行一系列操作,然后最終將瀏覽器關閉,這個整個過程叫做:一次會話。會話在服務器端也有一個對應的java對象,這個java對象叫做:session。

    • 什么是一次請求:用戶在瀏覽器上點擊了一下,然后到頁面停下來,可以粗略認為是一次請求。請求對應的服務器端的java對象是:request。

    • 一個會話當中包含多次請求。(一次會話對應N次請求。)

  • 在java的servlet規范當中,session對應的類名:HttpSession(jarkata.servlet.http.HttpSession)

  • session機制屬于B/S結構的一部分。如果使用php語言開發WEB項目,同樣也是有session這種機制的。session機制實際上是一個規范。然后不同的語言對這種會話機制都有實現。

  • session對象最主要的作用是:保存會話狀態。(用戶登錄成功了,這是一種登錄成功的狀態,你怎么把登錄成功的狀態一直保存下來呢?使用session對象可以保留會話狀態。)

  • 為什么需要session對象來保存會話狀態呢?

    • 因為HTTP協議是一種無狀態協議。

    • 什么是無狀態:請求的時候,B和S是連接的,但是請求結束之后,連接就斷了。為什么要這么做?HTTP協議為什么要設計成這樣?因為這樣的無狀態協議,可以降低服務器的壓力。請求的瞬間是連接的,請求結束之后,連接斷開,這樣服務器壓力小。

    • 只要B和S斷開了,那么關閉瀏覽器這個動作,服務器知道嗎?

      • 不知道。服務器是不知道瀏覽器關閉的。

  • 張三打開一個瀏覽器A,李四打開一個瀏覽器B,訪問服務器之后,在服務器端會生成:

    • 張三專屬的session對象

    • 李四專屬的session對象

  • 為什么不使用request對象保存會話狀態?為什么不使用ServletContext對象保存會話狀態?

    • request.setAttribute()存,request.getAttribute()取,ServletContext也有這個方法。request是請求域。ServletContext是應用域。

    • request是一次請求一個對象。

    • ServletContext對象是服務器啟動的時候創建,服務器關閉的時候銷毀,這個ServletContext對象只有一個。

    • ServletContext對象的域太大。

    • request請求域(HttpServletRequest)、session會話域(HttpSession)、application域(ServletContext)

    • request < session < application

  • 思考一下:session對象的實現原理。

    • HttpSession session = request.getSession();

    • 這行代碼很神奇。張三訪問的時候獲取的session對象就是張三的。李四訪問的時候獲取的session對象就是李四的。

  • session的實現原理:

    • JSESSIONID=xxxxxx 這個是以Cookie的形式保存在瀏覽器的內存中的。瀏覽器只要關閉。這個cookie就沒有了。

    • session列表是一個Map,map的key是sessionid,map的value是session對象。

    • 用戶第一次請求,服務器生成session對象,同時生成id,將id發送給瀏覽器。

    • 用戶第二次請求,自動將瀏覽器內存中的id發送給服務器,服務器根據id查找session對象。

    • 關閉瀏覽器,內存消失,cookie消失,sessionid消失,會話等同于結束。

  • Cookie禁用了,session還能找到嗎?

    • cookie禁用是什么意思?服務器正常發送cookie給瀏覽器,但是瀏覽器不要了。拒收了。并不是服務器不發了。

    • 找不到了。每一次請求都會獲取到新的session對象。

    • cookie禁用了,session機制還能實現嗎?

      • 可以。需要使用URL重寫機制。

      • http://localhost:8080/servlet12/test/session;jsessionid=19D1C99560DCBF84839FA43D58F56E16

      • URL重寫機制會提高開發者的成本。開發人員在編寫任何請求路徑的時候,后面都要添加一個sessionid,給開發帶來了很大的難度,很大的成本。所以大部分的網站都是這樣設計的:你要是禁用cookie,你就別用了。

  • 總結一下到目前位置我們所了解的域對象:

    • request(對應的類名:HttpServletRequest)

      • 請求域(請求級別的)

    • session(對應的類名:HttpSession)

      • 會話域(用戶級別的)

    • application(對應的類名:ServletContext)

      • 應用域(項目級別的,所有用戶共享的。)

    • 這三個域對象的大小關系

      • request < session < application

    • 他們三個域對象都有以下三個公共的方法:

      • setAttribute(向域當中綁定數據)

      • getAttribute(從域當中獲取數據)

      • removeAttribute(刪除域當中的數據)

    • 使用原則:盡量使用小的域。

  • session掌握之后,我們怎么解決oa項目中的登錄問題,怎么能讓登錄起作用。

    • 登錄成功之后,可以將用戶的登錄信息存儲到session當中。也就是說session中如果有用戶的信息就代表用戶登錄成功了。session中沒有用戶信息,表示用戶沒有登錄過。則跳轉到登錄頁面。

  • 銷毀session對象:

    • session.invalidate();
      

Cookie

  • session的實現原理中,每一個session對象都會關聯一個sessionid,例如:

    • JSESSIONID=41C481F0224664BDB28E95081D23D5B8

    • 以上的這個鍵值對數據其實就是cookie對象。

    • 對于session關聯的cookie來說,這個cookie是被保存在瀏覽器的“運行內存”當中。

    • 只要瀏覽器不關閉,用戶再次發送請求的時候,會自動將運行內存中的cookie發送給服務器。

    • 例如,這個Cookie: JSESSIONID=41C481F0224664BDB28E95081D23D5B8就會再次發送給服務器。

    • 服務器就是根據41C481F0224664BDB28E95081D23D5B8這個值來找到對應的session對象的。

  • cookie怎么生成?cookie保存在什么地方?cookie有啥用?瀏覽器什么時候會發送cookie,發送哪些cookie給服務器???????

  • cookie最終是保存在瀏覽器客戶端上的。

    • 可以保存在運行內存中。(瀏覽器只要關閉cookie就消失了。)

    • 也可以保存在硬盤文件中。(永久保存。)

  • cookie有啥用呢?

    • cookie和session機制其實都是為了保存會話的狀態。

    • cookie是將會話的狀態保存在瀏覽器客戶端上。(cookie數據存儲在瀏覽器客戶端上的。)

    • session是將會話的狀態保存在服務器端上。(session對象是存儲在服務器上。)

    • 為什么要有cookie和session機制呢?因為HTTP協議是無狀態 無連接協議。

  • cookie的經典案例

    • 京東商城,在未登錄的情況下,向購物車中放幾件商品。然后關閉商城,再次打開瀏覽器,訪問京東商城的時候,購物車中的商品還在,這是怎么做的?我沒有登錄,為什么購物車中還有商品呢?

      • 將購物車中的商品編號放到cookie當中,cookie保存在硬盤文件當中。這樣即使關閉瀏覽器。硬盤上的cookie還在。下一次再打開京東商城的時候,查看購物車的時候,會自動讀取本地硬盤中存儲的cookie,拿到商品編號,動態展示購物車中的商品。

        • 京東存儲購物車中商品的cookie可能是這樣的:productIds=xxxxx,yyyy,zzz,kkkk

        • 注意:cookie如果清除掉,購物車中的商品就消失了。

    • 126郵箱中有一個功能:十天內免登錄

      • 這個功能也是需要cookie來實現的。

      • 怎么實現的呢?

        • 用戶輸入正確的用戶名和密碼,并且同時選擇十天內免登錄。登錄成功后。瀏覽器客戶端會保存一個cookie,這個cookie中保存了用戶名和密碼等信息,這個cookie是保存在硬盤文件當中的,十天有效。在十天內用戶再次訪問126的時候,瀏覽器自動提交126的關聯的cookie給服務器,服務器接收到cookie之后,獲取用戶名和密碼,驗證,通過之后,自動登錄成功。

        • 怎么讓cookie失效?

          • 十天過后自動失效。

          • 或者改密碼。

          • 或者在客戶端瀏覽器上清除cookie。

  • cookie機制和session機制其實都不屬于java中的機制,實際上cookie機制和session機制都是HTTP協議的一部分。php開發中也有cookie和session機制,只要是你是做web開發,不管是什么編程語言,cookie和session機制都是需要的。

  • HTTP協議中規定:任何一個cookie都是由name和value組成的。name和value都是字符串類型的。

  • 在java的servlet中,對cookie提供了哪些支持呢?

    • 提供了一個Cookie類來專門表示cookie數據。jakarta.servlet.http.Cookie;

    • java程序怎么把cookie數據發送給瀏覽器呢?response.addCookie(cookie);

  • 在HTTP協議中是這樣規定的:當瀏覽器發送請求的時候,會自動攜帶該path下的cookie數據給服務器。(URL。)

  • 關于cookie的有效時間

    • 怎么用java設置cookie的有效時間

      • cookie.setMaxAge(60 * 60); 設置cookie在一小時之后失效。

    • 沒有設置有效時間:默認保存在瀏覽器的運行內存中,瀏覽器關閉則cookie消失。

    • 只要設置cookie的有效時間 > 0,這個cookie一定會存儲到硬盤文件當中。

    • 設置cookie的有效時間 = 0 呢?

      • cookie被刪除,同名cookie被刪除。

    • 設置cookie的有效時間 < 0 呢?

      • 保存在運行內存中。和不設置一樣。

  • 關于cookie的path,cookie關聯的路徑:

    • 假設現在發送的請求路徑是“http://localhost:8080/servlet13/cookie/generate”生成的cookie,如果cookie沒有設置path,默認的path是什么?

      • 默認的path是:http://localhost:8080/servlet13/cookie 以及它的子路徑。

      • 也就是說,以后只要瀏覽器的請求路徑是http://localhost:8080/servlet13/cookie 這個路徑以及這個路徑下的子路徑,cookie都會被發送到服務器。

    • 手動設置cookie的path

      • cookie.setPath(“/servlet13”); 表示只要是這個servlet13項目的請求路徑,都會提交這個cookie給服務器。

  • 瀏覽器發送cookie給服務器了,服務器中的java程序怎么接收?

    • Cookie[] cookies = request.getCookies(); // 這個方法可能返回null
      if(cookies != null){for(Cookie cookie : cookies){// 獲取cookie的nameString name = cookie.getName();// 獲取cookie的valueString value = cookie.getValue();}
      }
  • 使用cookie實現一下十天內免登錄功能。

    • 先實現登錄功能

      • 登錄成功

        • 跳轉到部門列表頁面

      • 登錄失敗

        • 跳轉到登錄失敗頁面

    • 修改前端頁面

      • 在登錄頁面給一個復選框,復選框后面給一句話:十天內免登錄。

      • 用戶選擇了復選框:表示要支持十天內免登錄。

      • 用戶沒有選擇復選框:表示用戶不想使用十天內免登錄功能。

    • 修改Servlet中的login方法

      • 如果用戶登錄成功了,并且用戶登錄時選擇了十天內免登錄功能,這個時候應該在Servlet的login方法中創建cookie,用來存儲用戶名和密碼,并且設置路徑,設置有效期,將cookie響應給瀏覽器。(瀏覽器將其自動保存在硬盤文件當中10天)

    • 用戶再次訪問該網站的時候,訪問這個網站的首頁的時候,有兩個走向:

      • 要么跳轉到部門列表頁面

      • 要么跳轉到登錄頁面

      • 以上分別有兩個走向,這顯然是需要編寫java程序進行控制的。

JSP

  • 我的第一個JSP程序:

    • 在WEB-INF目錄之外創建一個index.jsp文件,然后這個文件中沒有任何內容。

  • 將上面的項目部署之后,啟動服務器,打開瀏覽器,訪問以下地址:

    • http://localhost:8080/jsp/index.jsp 展現在大家面前的是一個空白。

    • 實際上訪問以上的這個:index.jsp,底層執行的是:index_jsp.class 這個java程序。

    • 這個index.jsp會被tomcat翻譯生成index_jsp.java文件,然后tomcat服務器又會將index_jsp.java編譯生成index_jsp.class文件

    • 訪問index.jsp,實際上執行的是index_jsp.class中的方法。

  • JSP實際上就是一個Servlet。

    • index.jsp訪問的時候,會自動翻譯生成index_jsp.java,會自動編譯生成index_jsp.class,那么index_jsp 這就是一個類。

    • index_jsp 類繼承 HttpJspBase,而HttpJspBase類繼承的是HttpServlet。所以index_jsp類就是一個Servlet類。

    • jsp的生命周期和Servlet的生命周期完全相同。完全就是一個東西。沒有任何區別。

    • jsp和servlet一樣,都是單例的。(假單例。)

  • jsp文件第一次訪問的時候是比較慢的,為什么?

    • 為什么大部分的運維人員在給客戶演示項目的時候,為什么提前先把所有的jsp文件先訪問一遍。

    • 第一次比較麻煩:

      • 要把jsp文件翻譯生成java源文件

      • java源文件要編譯生成class字節碼文件

      • 然后通過class去創建servlet對象

      • 然后調用servlet對象的init方法

      • 最后調用servlet對象的service方法。

    • 第二次就比較快了,為什么?

      • 因為第二次直接調用單例servlet對象的service方法即可。

  • JSP是什么?

    • JSP是java程序。(JSP本質還是一個Servlet)

    • JSP是:JavaServer Pages的縮寫。(基于Java語言實現的服務器端的頁面。)

    • Servlet是JavaEE的13個子規范之一,那么JSP也是JavaEE的13個子規范之一。

    • JSP是一套規范。所有的web容器/web服務器都是遵循這套規范的,都是按照這套規范進行的“翻譯”

    • 每一個web容器/web服務器都會內置一個JSP翻譯引擎。

  • 對JSP進行錯誤調試的時候,還是要直接打開JSP文件對應的java文件,檢查java代碼。

  • 開發JSP的最高境界:

    • 眼前是JSP代碼,但是腦袋中呈現的是java代碼。

  • JSP既然本質上是一個Servlet,那么JSP和Servlet到底有什么區別呢?

    • 職責不同:

      • Servlet的職責是什么:收集數據。(Servlet的強項是邏輯處理,業務處理,然后鏈接數據庫,獲取/收集數據。)

      • JSP的職責是什么:展示數據。(JSP的強項是做數據的展示)

  • JSP的基礎語法

    • 在jsp文件中直接編寫文字,都會自動被翻譯到哪里?

      • 翻譯到servlet類的service方法的out.write("翻譯到這里"),直接翻譯到雙引號里,被java程序當做普通字符串打印輸出到瀏覽器。

      • 在JSP中編寫的HTML CSS JS代碼,這些代碼對于JSP來說只是一個普通的字符串。但是JSP把這個普通的字符串一旦輸出到瀏覽器,瀏覽器就會對HTML CSS JS進行解釋執行。展現一個效果。

    • JSP的page指令(這個指令后面再詳細說,這里先解決一下中文亂碼問題),解決響應時的中文亂碼問題:

      • 通過page指令來設置響應的內容類型,在內容類型的最后面添加:charset=UTF-8

        • <%@page contentType="text/html;charset=UTF-8"%>,表示響應的內容類型是text/html,采用的字符集UTF-8

        • <%@page import="java.util.List,java.util.ArrayList"%>

    • 怎么在JSP中編寫Java程序:

      • <% java語句; %>

        • 在這個符號當中編寫的被視為java程序,被翻譯到Servlet類的service方法內部。

        • 這里你要細心點,你要思考,在<% %>這個符號里面寫java代碼的時候,你要時時刻刻的記住你正在“方法體”當中寫代碼,方法體中可以寫什么,不可以寫什么,你心里是否明白呢?

        • 在service方法當中編寫的代碼是有順序的,方法體當中的代碼要遵循自上而下的順序依次逐行執行。

        • service方法當中不能寫靜態代碼塊,不能寫方法,不能定義成員變量。。。。。。

        • 在同一個JSP當中 <%%> 這個符號可以出現多個。

      • <%! %>

        • 在這個符號當中編寫的java程序會自動翻譯到service方法之外。

        • 這個語法很少用,為什么?不建議使用,因為在service方法外面寫靜態變量和實例變量,都會存在線程安全問題,因為JSP就是servlet,servlet是單例的,多線程并發的環境下,這個靜態變量和實例變量一旦有修改操作,必然會存在線程安全問題。

      • JSP的輸出語句

        • 怎么向瀏覽器上輸出一個java變量。

        • <% String name = “jack”; out.write("name = " + name); %>

        • 注意:以上代碼中的out是JSP的九大內置對象之一。可以直接拿來用。當然,必須只能在service方法內部使用。

        • 如果向瀏覽器上輸出的內容中沒有“java代碼”,例如輸出的字符串是一個固定的字符串,可以直接在jsp中編寫,不需要寫到<%%> 這里。

        • 如果輸出的內容中含有“java代碼”,這個時候可以使用以下語法格式:

          • <%= %> 注意:在=的后面編寫要輸出的內容。

          • <%= %> 這個符號會被翻譯到哪里?最終翻譯成什么?

            • 翻譯成了這個java代碼: out.print();

            • 翻譯到service方法當中了。

          • 什么時候使用<%=%> 輸出呢?輸出的內容中含有java的變量,輸出的內容是一個動態的內容,不是一個死的字符串。如果輸出的是一個固定的字符串,直接在JSP文件中編寫即可。

    • 在JSP中如何編寫JSP的專業注釋

      • <%--JSP的專業注釋,不會被翻譯到java源代碼當中。--%>

      • <!--這種注釋屬于HTML的注釋,這個注釋信息仍然會被翻譯到java源代碼當中,不建議。-->

    • JSP基礎語法總結:

      • JSP中直接編寫普通字符串

        • 翻譯到service方法的out.write("這里")

      • <%%>

        • 翻譯到service方法體內部,里面是一條一條的java語句。

      • <%! %>

        • 翻譯到service方法之外。

      • <%= %>

        • 翻譯到service方法體內部,翻譯為:out.print();

      • <%@page contentType="text/html;charset=UTF-8"%>

        • page指令,通過contentType屬性用來設置響應的內容類型。

    • 使用Servlet + JSP完成oa項目的改造。

      • 使用Servlet處理業務,收集數據。 使用JSP展示數據。

      • 將之前原型中的html文件,全部修改為jsp,然后在jsp文件頭部添加page指令(指定contentType防止中文亂碼),將所有的JSP直接拷貝到web目錄下。

      • 完成所有頁面的正常流轉。(頁面仍然能夠正常的跳轉。修改超鏈接的請求路徑。)

        • <%=request.getContextPath() %> 在JSP中動態的獲取應用的根路徑。

      • Servlet中連接數據庫,查詢所有的部門,遍歷結果集。

        • 遍歷結果集的過程中,取出部門編號、部門名、位置等信息,封裝成java對象。

        • 將java對象存放到List集合中。

        • 將List集合存儲到request域當中。

        • 轉發forward到jsp。

      • 在JSP中:

        • 從request域當中取出List集合。

        • 遍歷List集合,取出每個部門對象。動態生成tr。

      • 思考一個問題:如果我只用JSP這一個技術,能不能開發web應用?

        • 當然可以使用JSP來完成所有的功能。因為JSP就是Servlet,在JSP的<%%>里面寫的代碼就是在service方法當中的,所以在<%%>當中完全可以編寫JDBC代碼,連接數據庫,查詢數據,也可以在這個方法當中編寫業務邏輯代碼,處理業務,都是可以的,所以使用單獨的JSP開發web應用完全沒問題。

        • 雖然JSP一個技術就可以完成web應用,但是不建議,還是建議采用servlet + jsp的方式進行開發。這樣都能將各自的優點發揮出來。JSP就是做數據展示。Servlet就是做數據的收集。(JSP中編寫的Java代碼越少越好。)一定要職責分明。

      • JSP文件的擴展名必須是xxx.jsp嗎?

        • jsp文件的擴展名是可以配置的。不是固定的。

        • 在CATALINA_HOME/conf/web.xml,在這個文件當中配置jsp文件的擴展名。

        • <servlet-mapping><servlet-name>jsp</servlet-name><url-pattern>*.jsp</url-pattern><url-pattern>*.jspx</url-pattern>
          </servlet-mapping>
          
        • xxx.jsp文件對于小貓咪來說,只是一個普通的文本文件,web容器會將xxx.jsp文件最終生成java程序,最終調用的是java對象相關的方法,真正執行的時候,和jsp文件就沒有關系了。

        • 小竅門:JSP如果看不懂,建議把jsp翻譯成java代碼,就能看懂了。

      • 同學問:包名bean是什么意思?

        • javabean(java的logo是一杯冒著熱氣的咖啡。javabean被翻譯為:咖啡豆)

        • java是一杯咖啡,咖啡又是由一粒一粒的咖啡豆研磨而成。

        • 整個java程序中有很多bean的存在。由很多bean組成。

        • 什么是javabean?實際上javabean你可以理解為符合某種規范的java類,比如:

          • 有無參數構造方法

          • 屬性私有化

          • 對外提供公開的set和get方法

          • 實現java.io.Serializable接口

          • 重寫toString

          • 重寫hashCode+equals

          • ....

        • javabean其實就是java中的實體類。負責數據的封裝。

        • 由于javabean符合javabean規范,具有更強的通用性。

      • 完成剩下所有功能的改造。

  • 當前的oa應用存在的問題:

    • 任何一個用戶都可以訪問這個系統,都可以對這個系統當中的數據進行增刪改這些危險的操作。我只想讓合法的用戶去使用這個系統,不合法的用戶不能訪問這個系統,怎么辦?

      • 加一個登錄功能。登錄成功的可以訪問該系統,登錄失敗不能訪問。

    • 實現登錄功能:

      • 步驟1:數據庫當中添加一個用戶表:t_user

        • t_user表當中存儲的是用戶的登錄信息,最基本的也包括:登錄的用戶名和登錄的密碼。

        • 密碼一般在數據庫表當中存儲的是密文。一般不以明文的形式存儲。(這里先使用明文方式。)

        • 向t_user表中插入數據。

      • 步驟2:再實現一個登錄頁面。

        • 登錄頁面上應該有一個登錄的表單。有用戶名和密碼輸入的框。

        • 用戶點擊登錄,提交表單,提交用戶名和密碼。form是post方式提交。

      • 步驟3:后臺要有一個對應的Servlet來處理登錄的請求。

        • 登錄成功:跳轉到部門列表頁面。

        • 登錄失敗:跳轉到失敗的頁面。

      • 步驟4:再提供一個登錄失敗的頁面。

  • 登錄功能實現了,目前存在的最大的問題:

    • 這個登錄功能目前只是一個擺設,沒有任何作用。只要用戶知道后端的請求路徑,照樣可以在不登錄的情況下訪問。

    • 這個登錄沒有真正起到攔截的作用。怎么解決?

  • JSP的指令

    • 指令的作用:指導JSP的翻譯引擎如何工作(指導當前的JSP翻譯引擎如何翻譯JSP文件。)

    • 指令包括哪些呢?

      • include指令:包含指令,在JSP中完成靜態包含,很少用了。(這里不講)

      • taglib指令:引入標簽庫的指令。這個到JJSTL標簽庫的時候再學習。現在先不管。

      • page指令:目前重點學習一個page指令。

    • 指令的使用語法是什么?

      • <%@指令名 屬性名=屬性值 屬性名=屬性值 屬性名=屬性值....%>

    • 關于page指令當中都有哪些常用的屬性呢?

      • <%@page session="true|false" %>
        true表示啟用JSP的內置對象session,表示一定啟動session對象。沒有session對象會創建。
        如果沒有設置,默認值就是session="true"
        session="false" 表示不啟動內置對象session。當前JSP頁面中無法使用內置對象session。
        
      • <%@page contentType="text/json" %>
        contentType屬性用來設置響應的內容類型
        但同時也可以設置字符集。
        <%@page contentType="text/json;charset=UTF-8" %>
        
      • <%@page pageEncoding="UTF-8" %>
        pageEncoding="UTF-8" 表示設置響應時采用的字符集。
        
      • <%@page import="java.util.List, java.util.Date, java.util.ArrayList" %>
        <%@page import="java.util.*" %>
        import語句,導包。
        
      • <%@page errorPage="/error.jsp" %>
        當前頁面出現異常之后,跳轉到error.jsp頁面。
        errorPage屬性用來指定出錯之后的跳轉位置。
        
      • <%@page isErrorPage="true" %>
        表示啟用JSP九大內置對象之一:exception
        默認值是false。
        
  • JSP的九大內置對象

    • jakarta.servlet.jsp.PageContext pageContext 頁面作用域

    • jakarta.servlet.http.HttpServletRequest request 請求作用域

    • jakarta.servlet.http.HttpSession session 會話作用域

    • jakarta.servlet.ServletContext application 應用作用域

      • pageContext < request < session < application

      • 以上四個作用域都有:setAttribute、getAttribute、removeAttribute方法。

      • 以上作用域的使用原則:盡可能使用小的域。

    • java.lang.Throwable exception

    • jakarta.servlet.ServletConfig config

    • java.lang.Object page (其實是this,當前的servlet對象)

    • jakarta.servlet.jsp.JspWriter out (負責輸出)

    • jakarta.servlet.http.HttpServletResponse response (負責響應)

EL表達式

  • EL表達式是干什么用的?

    • Expression Language(表達式語言)

    • EL表達式可以代替JSP中的java代碼,讓JSP文件中的程序看起來更加整潔,美觀。

    • JSP中夾雜著各種java代碼,例如<% java代碼 %>、<%=%>等,導致JSP文件很混亂,不好看,不好維護。所以才有了后期的EL表達式。

    • EL表達式可以算是JSP語法的一部分。EL表達式歸屬于JSP。

  • EL表達式出現在JSP中主要是:

    • 從某個作用域中取數據,然后將其轉換成字符串,然后將其輸出到瀏覽器。這就是EL表達式的功效。三大功效:

      • 第一功效:從某個域中取數據。

        • 四個域:

          • pageContext

          • request

          • session

          • application

      • 第二功效:將取出的數據轉成字符串。

        • 如果是一個java對象,也會自動調用java對象的toString方法將其轉換成字符串。

      • 第三功效:將字符串輸出到瀏覽器。

        • 和這個一樣:<%= %>,將其輸出到瀏覽器。

  • EL表達式很好用,基本的語法格式:

    • ${表達式}

  • EL表達式的使用:

    • <%// 創建User對象User user = new User();user.setUsername("jackson");user.setPassword("1234");user.setAge(50);// 將User對象存儲到某個域當中。一定要存,因為EL表達式只能從某個范圍中取數據。// 數據是必須存儲到四大范圍之一的。request.setAttribute("userObj", user);
      %><%--使用EL表達式取--%>
      ${這個位置寫什么????這里寫的一定是存儲到域對象當中時的name}
      要這樣寫:
      ${userObj}
      等同于java代碼:<%=request.getAttribute("userObj")%>
      你不要這樣寫:${"userObj"}面試題:${abc} 和 ${"abc"}的區別是什么?${abc}表示從某個域中取出數據,并且被取的這個數據的name是"abc",之前一定有這樣的代碼: 域.setAttribute("abc", 對象);${"abc"} 表示直接將"abc"當做普通字符串輸出到瀏覽器。不會從某個域中取數據了。${userObj} 底層是怎么做的?從域中取數據,取出user對象,然后調用user對象的toString方法,轉換成字符串,輸出到瀏覽器。<%--如果想輸出對象的屬性值,怎么辦?--%>
      ${userObj.username} 使用這個語法的前提是:User對象有getUsername()方法。
      ${userObj.password} 使用這個語法的前提是:User對象有getPassword()方法。
      ${userObj.age} 使用這個語法的前提是:User對象有getAge()方法。
      ${userObj.email} 使用這個語法的前提是:User對象有getEmail()方法。
      EL表達式中的. 這個語法,實際上調用了底層的getXxx()方法。
      注意:如果沒有對應的get方法,則出現異常。報500錯誤。${userObj.addr222.zipcode}
      以上EL表達式對應的java代碼:
      user.getAddr222().getZipcode()
      
    • EL表達式優先從小范圍中讀取數據。

      • pageContext < request < session < application

    • EL表達式中有四個隱含的隱式的范圍:

      • pageScope 對應的是 pageContext范圍。

      • requestScope 對應的是 request范圍。

      • sessionScope 對應的是 session范圍。

      • applicationScope 對應的是 application范圍。

    • EL表達式對null進行了預處理。如果是null,則向瀏覽器輸出一個空字符串。

    • EL表達式取數據的時候有兩種形式:

      • 第一種:. (大部分使用這種方式)

      • 第二種:[ ] (如果存儲到域的時候,這個name中含有特殊字符,可以使用 [ ])

        • request.setAttribute("abc.def", "zhangsan");

        • ${requestScope.abc.def} 這樣是無法取值的。

        • 應該這樣:${requestScope["abc.def"]}

    • 掌握使用EL表達式,怎么從Map集合中取數據:

      • ${map.key}

    • 掌握使用EL表達式,怎么從數組和List集合中取數據:

      • ${數組[0]}

      • ${數組[1]}

      • ${list[0]}

    • page指令當中,有一個屬性,可以忽略EL表達式

      • <%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>
        isELIgnored="true" 表示忽略EL表達式
        isELIgnored="false" 表示不忽略EL表達式。(這是默認值)isELIgnored="true" 這個是全局的控制。可以使用反斜杠進行局部控制:\${username} 這樣也可以忽略EL表達式。
        
    • 通過EL表達式獲取應用的根:

      • ${pageContext.request.contextPath}

    • EL表達式中其他的隱式對象:

      • pageContext

      • param

      • paramValues

      • initParam

    • EL表達式的運算符

      • 算術運算符

        • +、-、*、/、%

      • 關系運算符

        • == eq != > >= < <=
      • 邏輯運算符

        • ! && || not and or
      • 條件運算符

        • ? :
      • 取值運算符

        • [ ]和.

      • empty運算符

        • empty運算符的結果是boolean類型
        • ${empty param.username}
        • ${not empty param.username}
        • ${!empty param.password}

JSTL標簽庫

  • 什么是JSTL標簽庫?

    • Java Standard Tag Lib(Java標準的標簽庫)

    • JSTL標簽庫通常結合EL表達式一起使用。目的是讓JSP中的java代碼消失。

    • 標簽是寫在JSP當中的,但實際上最終還是要執行對應的java程序。(java程序在jar包當中。)

  • 使用JSTL標簽庫的步驟:

    • 第一步:引入JSTL標簽庫對應的jar包。

      • tomcat10之后引入的jar包是:

        • jakarta.servlet.jsp.jstl-2.0.0.jar

        • jakarta.servlet.jsp.jstl-api-2.0.0.jar

      • 在IDEA當中怎么引入?

        • 在WEB-INF下新建lib目錄,然后將jar包拷貝到lib當中。然后將其“Add Lib...”

        • 一定是要和mysql的數據庫驅動一樣,都是放在WEB-INF/lib目錄下的。

        • 什么時候需要將jar包放到WEB-INF/lib目錄下?如果這個jar是tomcat服務器沒有的。

    • 第二步:在JSP中引入要使用標簽庫。(使用taglib指令引入標簽庫。)

      • JSTL提供了很多種標簽,你要引入哪個標簽????重點掌握核心標簽庫。

      • <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
        這個就是核心標簽庫。
        prefix="這里隨便起一個名字就行了,核心標簽庫,大家默認的叫做c,你隨意。"
        
    • 第三步:在需要使用標簽的位置使用即可。表面使用的是標簽,底層實際上還是java程序。

  • JSTL標簽的原理

    • <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      以上uri后面的路徑實際上指向了一個xxx.tld文件。
      tld文件實際上是一個xml配置文件。
      在tld文件中描述了“標簽”和“java類”之間的關系。
      以上核心標簽庫對應的tld文件是:c.tld文件。它在哪里。
      在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目錄下,有一個c.tld文件。
      
    • 源碼解析:配置文件tld解析

      • <tag><description>對該標簽的描述</description><name>catch</name> 標簽的名字<tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class> 標簽對應的java類。<body-content>JSP</body-content> 標簽體當中可以出現的內容,如果是JSP,就表示標簽體中可以出現符合JSP所有語法的代碼。例如EL表達式。<attribute><description>對這個屬性的描述</description><name>var</name> 屬性名<required>false</required> false表示該屬性不是必須的。true表示該屬性是必須的。<rtexprvalue>false</rtexprvalue> 這個描述說明了該屬性是否支持EL表達式。false表示不支持。true表示支持EL表達式。</attribute></tag><c:catch var="">JSP....
        </c:catch>
        
    • jstl中的核心標簽庫core當中有哪些常用的標簽呢?

      • c:if

        • <c:if test="boolean類型,支持EL表達式"></c: if>

      • c:forEach

        • <c:forEach items="集合,支持EL表達式" var="集合中的元素" varStatus="元素狀態對象"> ${元素狀態對象.count} </c: forEach>

        • <c:forEach var="i" begin="1" end="10" step="2"> ${i} </c: forEach>

      • c:choose c:when c:otherwise

        • <c:choose><c:when test="${param.age < 18}">青少年</c:when><c:when test="${param.age < 35}">青年</c:when><c:when test="${param.age < 55}">中年</c:when><c:otherwise>老年</c:otherwise>
          </c:choose>
          

改造OA

  • 使用什么技術改造呢?

    • Servlet + JSP + EL表達式 + JSTL標簽。進行改造。

  • 在前端HTML代碼中,有一個標簽,叫做base標簽,這個標簽可以設置整個網頁的基礎路徑。

    • 這是Java的語法,也不是JSP的語法。是HTML中的一個語法。HTML中的一個標簽。通常出現在head標簽中。

    • < base href="http://localhost:8080/oa/">

    • 在當前頁面中,凡是路徑沒有以“/”開始的,都會自動將base中的路徑添加到這些路徑之前。

      • < a href="ab/def"></ a>

      • 等同于:< a href="http://localhost:8080/oa/ab/def"></ a>

    • 需要注意:在JS代碼中的路徑,保險起見,最好不要依賴base標簽。JS代碼中的路徑最好寫上全路徑。

    • <base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
      

Filter過濾器

  • 當前的OA項目存在什么缺陷?

    • DeptServlet、EmpServlet、OrderServlet。每一個Servlet都是處理自己相關的業務。在這些Servlet執行之前都是需要判斷用戶是否登錄了。如果用戶登錄了,可以繼續操作,如果沒有登錄,需要用戶登錄。這段判斷用戶是否登錄的代碼是固定的,并且在每一個Servlet類當中都需要編寫,顯然代碼沒有得到重復利用。包括每一個Servlet都要解決中文亂碼問題,也有公共的代碼。這些代碼目前都是重復編寫,并沒有達到復用。怎么解決這個問題?

      • 可以使用Servlet規范中的Filter過濾器來解決這個問題。

  • Filter是什么,有什么用,執行原理是什么?

    • Filter是過濾器。

    • Filter可以在Servlet這個目標程序執行之前添加代碼。也可以在目標Servlet執行之后添加代碼。之前之后都可以添加過濾規則。

    • 一般情況下,都是在過濾器當中編寫公共代碼。

  • 一個過濾器怎么寫呢?

    • 第一步:編寫一個Java類實現一個接口:jarkata.servlet.Filter。并且實現這個接口當中所有的方法。

      • init方法:在Filter對象第一次被創建之后調用,并且只調用一次。

      • doFilter方法:只要用戶發送一次請求,則執行一次。發送N次請求,則執行N次。在這個方法中編寫過濾規則。

      • destroy方法:在Filter對象被釋放/銷毀之前調用,并且只調用一次。

    • 第二步:在web.xml文件中對Filter進行配置。這個配置和Servlet很像。

      • <filter><filter-name>filter2</filter-name><filter-class>com.bjpowernode.javaweb.servlet.Filter2</filter-class>
        </filter>
        <filter-mapping><filter-name>filter2</filter-name><url-pattern>*.do</url-pattern>
        </filter-mapping>
        
      • 或者使用注解:@WebFilter({"*.do"})

  • 注意:

    • Servlet對象默認情況下,在服務器啟動的時候是不會新建對象的。

    • Filter對象默認情況下,在服務器啟動的時候會新建對象。

    • Servlet是單例的。Filter也是單例的。(單實例。)

  • 目標Servlet是否執行,取決于兩個條件:

    • 第一:在過濾器當中是否編寫了:chain.doFilter(request, response); 代碼。

    • 第二:用戶發送的請求路徑是否和Servlet的請求路徑一致。

  • chain.doFilter(request, response); 這行代碼的作用:

    • 執行下一個過濾器,如果下面沒有過濾器了,執行最終的Servlet。

  • 注意:Filter的優先級,天生的就比Servlet優先級高。

    • /a.do 對應一個Filter,也對應一個Servlet。那么一定是先執行Filter,然后再執行Servlet。

  • 關于Filter的配置路徑:

    • /a.do、/b.do、/dept/save。這些配置方式都是精確匹配。

    • /* 匹配所有路徑。

    • *.do 后綴匹配。不要以 / 開始

    • /dept/* 前綴匹配。

  • 在web.xml文件中進行配置的時候,Filter的執行順序是什么?

    • 依靠filter-mapping標簽的配置位置,越靠上優先級越高。

  • 過濾器的調用順序,遵循棧數據結構。

  • 使用@WebFilter的時候,Filter的執行順序是怎樣的呢?

    • 執行順序是:比較Filter這個類名。

    • 比如:FilterA和FilterB,則先執行FilterA。

    • 比如:Filter1和Filter2,則先執行Filter1.

  • Filter的生命周期?

    • 和Servlet對象生命周期一致。

    • 唯一的區別:Filter默認情況下,在服務器啟動階段就實例化。Servlet不會。

  • Filter過濾器這里有一個設計模式:

    • 責任鏈設計模式。

    • 過濾器最大的優點:

      • 在程序編譯階段不會確定調用順序。因為Filter的調用順序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的順序就可以調整Filter的執行順序。顯然Filter的執行順序是在程序運行階段動態組合的。那么這種設計模式被稱為責任鏈設計模式。

    • 責任鏈設計模式最大的核心思想:

      • 在程序運行階段,動態的組合程序的調用順序。

  • 使用過濾器改造OA項目。

Listener監聽器

  • 什么是監聽器?

    • 監聽器是Servlet規范中的一員。就像Filter一樣。Filter也是Servlet規范中的一員。

    • 在Servlet中,所有的監聽器接口都是以“Listener”結尾。

  • 監聽器有什么用?

    • 監聽器實際上是Servlet規范留給我們javaweb程序員的特殊時機。

    • 特殊的時刻如果想執行這段代碼,你需要想到使用對應的監聽器。

  • Servlet規范中提供了哪些監聽器?

    • jakarta.servlet包下:

      • ServletContextListener

      • ServletContextAttributeListener

      • ServletRequestListener

      • ServletRequestAttributeListener

    • jakarta.servlet.http包下:

      • HttpSessionListener

      • HttpSessionAttributeListener

        • 該監聽器需要使用@WebListener注解進行標注。

        • 該監聽器監聽的是什么?是session域中數據的變化。只要數據變化,則執行相應的方法。主要監測點在session域對象上。

      • HttpSessionBindingListener

        • 該監聽器不需要使用@WebListener進行標注。

        • 假設User類實現了該監聽器,那么User對象在被放入session的時候觸發bind事件,User對象從session中刪除的時候,觸發unbind事件。

        • 假設Customer類沒有實現該監聽器,那么Customer對象放入session或者從session刪除的時候,不會觸發bind和unbind事件。

      • HttpSessionIdListener

        • session的id發生改變的時候,監聽器中的唯一一個方法就會被調用。

      • HttpSessionActivationListener

        • 監聽session對象的鈍化和活化的。

        • 鈍化:session對象從內存存儲到硬盤文件。

        • 活化:從硬盤文件把session恢復到內存。

  • 實現一個監聽器的步驟:以ServletContextListener為例。

    • 第一步:編寫一個類實現ServletContextListener接口。并且實現里面的方法。

      • void contextInitialized(ServletContextEvent event)
        void contextDestroyed(ServletContextEvent event)
        
    • 第二步:在web.xml文件中對ServletContextListener進行配置,如下:

      • <listener><listener-class>com.bjpowernode.javaweb.listener.MyServletContextListener</listener-class>
        </listener>
        
      • 當然,第二步也可以不使用配置文件,也可以用注解,例如:@WebListener

  • 注意:所有監聽器中的方法都是不需要javaweb程序員調用的,由服務器來負責調用?什么時候被調用呢?

    • 當某個特殊的事件發生(特殊的事件發生其實就是某個時機到了。)之后,被web服務器自動調用。

  • 思考一個業務場景:

    • 請編寫一個功能,記錄該網站實時的在線用戶的個數。

    • 我們可以通過服務器端有沒有分配session對象,因為一個session代表了一個用戶。有一個session就代表有一個用戶。如果你采用這種邏輯去實現的話,session有多少個,在線用戶就有多少個。這種方式的話:HttpSessionListener夠用了。session對象只要新建,則count++,然后將count存儲到ServletContext域當中,在頁面展示在線人數即可。

    • 業務發生改變了,只統計登錄的用戶的在線數量,這個該怎么辦?

      • session.setAttribute("user", userObj);

      • 用戶登錄的標志是什么?session中曾經存儲過User類型的對象。那么這個時候可以讓User類型的對象實現HttpSessionBindingListener監聽器,只要User類型對象存儲到session域中,則count++,然后將count++存儲到ServletContext對象中。頁面展示在線人數即可。

  • 實現oa項目中當前登錄在線的人數。

    • 什么代表著用戶登錄了?

      • session.setAttribute("user", userObj); User類型的對象只要往session中存儲過,表示有新用戶登錄。

    • 什么代表著用戶退出了?

      • session.removeAttribute("user"); User類型的對象從session域中移除了。

      • 或者有可能是session銷毀了。(session超時)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/714677.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/714677.shtml
英文地址,請注明出處:http://en.pswp.cn/news/714677.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《深入淺出紅黑樹:一起動手實現自平衡的二叉搜索樹》

一、分析 1. 紅黑樹的性質 紅黑樹是一種自平衡的二叉搜索樹&#xff0c;它具有以下五個性質&#xff1a; &#xff08;1&#xff09;節點是紅色或黑色。 &#xff08;2&#xff09;根節點是黑色。 &#xff08;3&#xff09;所有葉子節點&#xff08;NIL節點&#xff09;是…

探索數據宇宙:深入解析大數據分析與管理技術

?? 歡迎大家來訪Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭?&#xff5e;?? &#x1f31f;&#x1f31f; 歡迎各位親愛的讀者&#xff0c;感謝你們抽出寶貴的時間來閱讀我的文章。 我是Srlua&#xff0c;在這里我會分享我的知識和經驗。&#x…

第六課:NIO簡介

一、傳統BIO的缺點 BIO屬于同步阻塞行IO,在服務器的實現模型為&#xff0c;每一個連接都要對應一個線程。當客戶端有連接請求的時候&#xff0c;服務器端需要啟動一個新的線程與之對應處理&#xff0c;這個模型有很多缺陷。當客戶端不做出進一步IO請求的時候&#xff0c;服務器…

《Spring Security 簡易速速上手小冊》第4章 授權與角色管理(2024 最新版)

文章目錄 4.1 理解授權4.1.1 基礎知識詳解授權的核心授權策略方法級安全動態權限檢查 4.1.2 主要案例&#xff1a;基于角色的頁面訪問控制案例 Demo 4.1.3 拓展案例 1&#xff1a;自定義投票策略案例 Demo測試自定義投票策略 4.1.4 拓展案例 2&#xff1a;使用方法級安全進行細…

【flutter】加載指示器(loading indicator)阻止用戶在某個操作執行期間操作頁面

在Flutter中&#xff0c;通過顯示一個加載指示器&#xff08;loading indicator&#xff09;來阻止用戶在某個操作執行期間操作頁面。以下是一個簡單的示例代碼&#xff0c;演示了按鈕被點擊后執行某操作&#xff0c;在操作完成前顯示加載指示器&#xff0c;阻止用戶操作頁面&a…

c語言數據結構(5)——棧

歡迎來到博主的專欄——C語言數據結構 博主id&#xff1a;代碼小豪 文章目錄 棧棧的順序存儲結構棧的插入空棧的初始化棧的刪除判斷空棧讀取棧頂元素數據 實現順序棧的所有代碼棧的鏈式存儲結構鏈式棧的初始化鏈式棧的入棧操作鏈式棧的出棧操作 實現鏈式棧的所有代碼 棧 棧是…

學習網絡編程No.11【傳輸層協議之UDP】

引言&#xff1a; 北京時間&#xff1a;2023/11/20/9:17&#xff0c;昨天成功更文&#xff0c;上周實現了更文兩篇&#xff0c;所以這周再接再厲。當然做題任在繼續&#xff0c;而目前做題給我的感覺以套路和技巧偏多&#xff0c;還是那句話很多東西不經歷你就是不懂&#xff…

測試人員如何向開發人員準確清晰地描述問題?

測試人員向開發人員準確清晰地描述問題可以采取以下方法&#xff1a; 提供詳細的背景和上下文信息&#xff1a;描述問題發生的環境、前提條件和操作步驟&#xff0c;讓開發人員能夠了解問題出現的場景。明確問題的癥狀和表現&#xff1a;清楚地說明問題的具體表現&#xff0c;…

【Python】2. 基礎語法

常量和表達式 我們可以把 Python 當成一個計算器, 來進行一些算術運算. 注意: print 是一個 Python 內置的 函數, 這個稍后詳細介紹. 可以使用 - * / ( ) 等運算符進行算術運算. 先算乘除, 后算加減. 運算符和數字之間, 可以沒有空格, 也可以有多個空格. 但是一般習慣上寫一…

LDR6328芯片:智能家居時代的小家電充電革新者

在當今的智能家居時代&#xff0c;小家電的供電方式正變得越來越智能化和高效化。 利用PD&#xff08;Power Delivery&#xff09;芯片進行誘騙取電&#xff0c;為后端小家電提供穩定電壓的技術&#xff0c;正逐漸成為行業的新寵。在這一領域&#xff0c;LDR6328芯片以其出色的…

Qt下使用modbus-c庫實現PLC線圈/保持寄存器的讀寫

系列文章目錄 提示&#xff1a;這里是該系列文章的所有文章的目錄 第一章&#xff1a;Qt下使用ModbusTcp通信協議進行PLC線圈/保持寄存器的讀寫&#xff08;32位有符號數&#xff09; 第二章&#xff1a;Qt下使用modbus-c庫實現PLC線圈/保持寄存器的讀寫 文章目錄 系列文章目錄…

前端Vue3項目如何打包成Docker鏡像運行

將前端Vue3項目打包成Docker鏡像并運行包括幾個主要步驟&#xff1a;項目打包、編寫Dockerfile、構建鏡像和運行容器。下面是一個基本的流程&#xff1a; 1. 項目打包 首先&#xff0c;確保你的Vue3項目可以正常運行和打包。在項目根目錄下執行以下命令來打包你的Vue3項目&am…

nest.js使用nest-winston日志一

nest-winston文檔 nest-winston - npm 參考&#xff1a;nestjs中winston日志模塊使用 - 浮的blog - SegmentFault 思否 安裝 cnpm install --save nest-winston winstoncnpm install winston-daily-rotate-file 在main.ts中 import { NestFactory } from nestjs/core; im…

【5G 接口協議】GTP-U協議介紹

博主未授權任何人或組織機構轉載博主任何原創文章&#xff0c;感謝各位對原創的支持&#xff01; 博主鏈接 本人就職于國際知名終端廠商&#xff0c;負責modem芯片研發。 在5G早期負責終端數據業務層、核心網相關的開發工作&#xff0c;目前牽頭6G算力網絡技術標準研究。 博客…

mysql學習

查看glibc版本 ldd --version --mysql啟動失敗,嘗試啟動 1 查看錯誤日志,端口被占用,參數名寫錯,有不支持的參數 2 通過mysqld啟動 mysqld --default-filemy.cnf & 3 mysqld --no-defaults --basedir/user/local/mysql --datadir/data/mysql/3306/data/ --usermysql 4 str…

深入理解 Nginx 的負載均衡與反向代理

深入理解 Nginx 的負載均衡與反向代理 Nginx 是一個高性能的 HTTP 和反向代理服務器&#xff0c;也是一個 IMAP/POP3/SMTP 代理服務器。由于其出色的性能和靈活性&#xff0c;Nginx 已成為現代 web 架構中的重要組成部分&#xff0c;尤其是在處理高并發連接和大規模流量時。在…

找到數組的中間位置-1991-[簡單]

力扣 關鍵點 從題目中總結出公式 sum * 2 nums[i] total從左往右開始嘗試&#xff0c;尋找 i 位置滿足上面的公式&#xff0c;為什么從左開始&#xff0c;因為題目要求找到最左邊的一個用前綴和的概念來解&#xff0c;從左往右嘗試i位置的左邊所有數之和&#xff0c;右邊所有…

基礎小白快速入門Python------>模塊的作用和意義

模塊&#xff0c; 這個詞聽起來是如此的高大威猛&#xff0c;以至于萌新小白見了瑟瑟發抖&#xff0c;本草履蟲見了都直搖頭&#xff0c;好像聽上去很難的樣子&#xff0c;但是但是&#xff0c;年輕人&#xff0c;請聽本少年細細講述&#xff0c;他只是看起來很難&#xff0c;實…

GO-接口

1. 接口 在Go語言中接口&#xff08;interface&#xff09;是一種類型&#xff0c;一種抽象的類型。 interface是一組method的集合&#xff0c;接口做的事情就像是定義一個協議&#xff08;規則&#xff09;&#xff0c;只要一臺機器有洗衣服和甩干的功能&#xff0c;我就稱它…

【go語言開發】swagger安裝和使用

本文主要介紹go-swagger的安裝和使用&#xff0c;首先介紹如何安裝swagger&#xff0c;測試是否成功&#xff1b;然后列出常用的注釋和給出使用例子&#xff1b;最后生成接口文檔&#xff0c;并在瀏覽器上測試 文章目錄 安裝注釋說明常用注釋參考例子 文檔生成格式化文檔生成do…