關于直播
-
什么時間直播?
-
晚上8:00到10:00
-
-
每周直播幾天?
-
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
關于系統架構
-
系統架構包括什么形式?
-
C/S架構
-
B/S架構
-
-
C/S架構?
-
Client / Server(客戶端 / 服務器)
-
C/S架構的軟件或者說系統有哪些呢?
-
QQ(先去騰訊官網下載一個QQ軟件,幾十MB,然后把這個客戶端軟件安裝上去,然后輸入QQ號以及密碼,登錄之后,就可以和你的朋友聊天了,就可以使用這個軟件了。)
-
-
C/S架構的特點:需要安裝特定的客戶端軟件。
-
C/S架構的系統優點和缺點分別是什么?
-
優點:
-
速度快(軟件中的數據大部分都是集成到客戶端軟件當中的,很少量的數據從服務器端傳送過來,所以C/S結構的系統速度快)
-
體驗好(速度又快,界面又酷炫,當然體驗好了。)
-
界面酷炫(專門的語言去實現界面的,更加靈活。)
-
服務器壓力小(因為大量的數據都是集成在客戶端軟件當中,所以服務器只需要傳送很少的數據量,當然服務器壓力小。)
-
安全(因為大量的數據是集成在客戶端軟件當中的,并且客戶端有很多個,服務器雖然只有一個,就算服務器那邊地震了,火災了,服務器受損了,問題也不大,因為大量的數據在多個客戶端上有緩存,有存儲,所以從這個方面來說,C/S結構的系統比較安全。)
-
.....
-
-
缺點:
-
升級維護比較差勁。(升級維護比較麻煩。成本比較高。每一個客戶端軟件都需要升級。有一些軟件不是那么容易安裝的。)
-
-
-
-
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。在加上速度慢。)
-
不安全(所有的數據都在服務器上,只要服務器發生火災,地震等不可抗力,最終數據全部丟失。)
-
....
-
-
-
-
C/S和B/S結構的系統,哪個好,哪個不好?
-
這個問題問的沒有水平。并不是哪個好,哪個不好。不同結構的系統在不同的業務場景下有不同的適用場景。
-
娛樂性軟件建議使用?
-
C/S 結構
-
-
公司內部使用的一些業務軟件建議使用?
-
公司內部使用的系統,需要維護成本低。
-
公司內部使用的系統,不需要很酷炫。
-
公司內部使用的企業級系統主要是能夠進行數據的維護即可。
-
B/S 結構。
-
-
-
注意了:開發B/S結構的系統,其實就是開發網站,其實就是開發一個WEB系統。
-
開發一個WEB系統你需要會哪些技術?
-
WEB前端(運行在瀏覽器上的程序。)
-
HTML
-
CSS
-
JavaScript
-
-
WEB后端(WEB服務器端的程序。)
-
Java可以(Java做WEB開發我們稱為JavaWEB開發。JavaWEB開發最核心的規范:Servlet【Server Applet服務器端的Java小程序。】)
-
C語言也可以
-
C++也可以
-
Python也行
-
PHP也可以
-
....
-
-
-
-
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超時)
-
-