B/S結(jié)構(gòu)系統(tǒng)的會(huì)話機(jī)制(session)
-
B/S結(jié)構(gòu)系統(tǒng)的會(huì)話機(jī)制(session)
- 每博一文案
- 1. session 會(huì)話機(jī)制的概述
- 2. 什么是 session 的會(huì)話
- 3. session 的作用
- 4. session 的實(shí)現(xiàn)原理解釋
- 5. 補(bǔ)充: Cookie禁用了,session還能找到嗎 ?
- 6. 總結(jié)一下到目前位置我們所了解的域?qū)ο螅?/li>
- 7. oa 項(xiàng)目的優(yōu)化體驗(yàn):使用上 session 會(huì)話機(jī)制:
- 8. 總結(jié):
- 9. 最后:
每博一文案
你跑得快,22歲有個(gè)家,身邊全是贊嘆,你跑得慢,30歲還在路上追求夢(mèng)想。有的人為了車,房拼了一輩子,
有的人買輛摩托車走遍了大好江山。你想成為怎樣的人,過怎樣的生活,只要你不后悔就行。
并不是所有人都能在早上七點(diǎn)鐘起床的,也別拿一碗飯來衡量一個(gè)人的胃口的大小。
有的人喜歡狼吞虎咽,有的人喜歡細(xì)嚼慢咽,允許別人做,別人允許自己做自己。
一歲有一歲的味道,跟著自己的心就好。不是所有選擇都要做正確的選項(xiàng)的,只要你想,你可以選擇
你喜歡的選項(xiàng)。沿途的花會(huì)一直開,以后的路也是,祝你祝我。
1. session 會(huì)話機(jī)制的概述
在Web應(yīng)用程序中,我們經(jīng)常要跟蹤用戶身份。當(dāng)一個(gè)用戶登錄成功后,如果他繼續(xù)訪問其他頁面,Web程序如何才能識(shí)別出該用戶身份?
因?yàn)?strong>HTTP協(xié)議是一個(gè)無狀態(tài)協(xié)議,即Web應(yīng)用程序無法區(qū)分收到的兩個(gè)HTTP請(qǐng)求是否是同一個(gè)瀏覽器發(fā)出的。為了跟蹤用戶狀態(tài),服務(wù)器可以向?yàn)g覽器分配一個(gè)唯一ID,并以Cookie的形式發(fā)送到瀏覽器,瀏覽器在后續(xù)訪問時(shí)總是附帶此Cookie,這樣,服務(wù)器就可以識(shí)別用戶身份。
我們把這種基于唯一ID識(shí)別用戶身份的機(jī)制稱為Session。每個(gè)用戶第一次訪問服務(wù)器后,會(huì)自動(dòng)獲得一個(gè)Session ID。如果用戶在一段時(shí)間內(nèi)沒有訪問服務(wù)器,那么Session會(huì)自動(dòng)失效,下次即使帶著上次分配的Session ID訪問,服務(wù)器也認(rèn)為這是一個(gè)新用戶,會(huì)分配新的Session ID。
2. 什么是 session 的會(huì)話
會(huì)話對(duì)應(yīng)的英語單詞:session
當(dāng)用戶打開瀏覽器,進(jìn)行一系列操作,然后最終將瀏覽器關(guān)閉,這個(gè)整個(gè)過程叫做:一次會(huì)話。會(huì)話在服務(wù)器端也有一個(gè)對(duì)應(yīng)的java對(duì)象,這個(gè)java對(duì)象叫做:session。
什么是一次請(qǐng)求:用戶在瀏覽器上點(diǎn)擊了一下,然后到頁面停下來,可以粗略認(rèn)為是一次請(qǐng)求。請(qǐng)求對(duì)應(yīng)的服務(wù)器端的java對(duì)象是:request。 這里提前透露一點(diǎn)后面的內(nèi)容: session 對(duì)象是用服務(wù)器端生成的,所以這里是通過 request 請(qǐng)求的方式向服務(wù)器獲取到一個(gè) session 會(huì)話對(duì)象
- 一個(gè)會(huì)話當(dāng)中包含多次請(qǐng)求(一次會(huì)話對(duì)應(yīng)N次請(qǐng)求。)
這里我們可以打印顯示我們的 session 地址信息
package com.RainbowSea.session;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/session")
public class TestSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
HttpSession session = request.getSession();
response.setContentType("text/html;charSet=UTF-8");
PrintWriter out = response.getWriter();
out.println(" session對(duì)象: " + session);
}
}
從 session 對(duì)象當(dāng)中存在于: org.apache.catalina.session.StandardSession 的位置。
在Java的servlet 的規(guī)范當(dāng)中,session 對(duì)應(yīng)的類名為: HttpSession(jarkata.servlett.http.HttpSession) 。
注意:
sessioin 機(jī)制屬于 B/S結(jié)構(gòu)的一部分。如果使用php語言開發(fā)WEB項(xiàng)目,同樣也是有session這種機(jī)制的。session機(jī)制實(shí)際上是一個(gè)規(guī)范。然后不同的語言對(duì)這種會(huì)話機(jī)制都有實(shí)現(xiàn)。
獲取 sessoin 的對(duì)象方法:
// 注意: sessio 是存儲(chǔ)在服務(wù)器端的,所以我們這里使用的是 request 請(qǐng)求的方式,向服務(wù)器請(qǐng)求獲取到 session 對(duì)象
// 該訪問獲取到 session 對(duì)象,如果服務(wù)器端沒有 session 對(duì)象會(huì)自動(dòng)創(chuàng)建出 session 對(duì)象
HttpSession session = request.getSession();
// 獲取到 session 對(duì)象,(參數(shù)為 false )表示:如果服務(wù)器當(dāng)中沒有 session 是不會(huì)自動(dòng)創(chuàng)建的。
HttpSession session1 = request.getSession(false);
3. session 的作用
session對(duì)象最主要的作用是:保存會(huì)話狀態(tài)。(用戶登錄成功了,這是一種登錄成功的狀態(tài),你怎么把登錄成功的狀態(tài)一直保存下來呢?使用session對(duì)象可以保留會(huì)話狀態(tài)。)
那我們?yōu)槭裁葱枰猻ession 對(duì)象來保存會(huì)話狀態(tài)呢?
因?yàn)镠TTP協(xié)議是一種無狀態(tài)協(xié)議。
什么是無狀態(tài):請(qǐng)求的時(shí)候,B和S是連接的,但是請(qǐng)求結(jié)束之后,連接就斷了。為什么要這么做?HTTP協(xié)議為什么要設(shè)計(jì)成這樣?因?yàn)檫@樣的無狀態(tài)協(xié)議,可以降低服務(wù)器的壓力。請(qǐng)求的瞬間是連接的,請(qǐng)求結(jié)束之后,連接斷開,這樣服務(wù)器壓力小。
只要B和S斷開了,那么關(guān)閉瀏覽器這個(gè)動(dòng)作,服務(wù)器知道嗎?
因?yàn)?HTTP 協(xié)議是無狀態(tài)的連接的,所以當(dāng)我們關(guān)閉了 瀏覽器的時(shí)候,我們的服務(wù)器端是無法接收到瀏覽器被關(guān)閉的一個(gè)信息的。所以:我們的服務(wù)器自然也就無法知道瀏覽器關(guān)閉了。
一個(gè)會(huì)話對(duì)應(yīng)一個(gè) sessoin 對(duì)象,一個(gè) session 對(duì)應(yīng)上一個(gè) ID也就是 (JSESSIONID) 。
比如:張三打開一個(gè)瀏覽器 A,李四打開一個(gè)瀏覽器B,訪問服務(wù)器之后,在服務(wù)端會(huì)生成:
- 張三專屬的session對(duì)象,同時(shí)會(huì)標(biāo)記上一個(gè) 對(duì)應(yīng)的 ID 信息
- 李四專屬的session對(duì)象 ,同時(shí)會(huì)標(biāo)記上一個(gè)對(duì)應(yīng)的 ID 信息。
- 注意了:這兩者之間的 ID信息是不一樣的。
代碼舉例:
package com.RainbowSea.serssion;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/test/session")
public class TestSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// request 和 session 都是在服務(wù)端的java對(duì)象,都在JVM當(dāng)中
// request對(duì)象代表一次請(qǐng)求,(一次請(qǐng)求對(duì)應(yīng)一個(gè)request對(duì)象,再次請(qǐng)求就會(huì)對(duì)應(yīng)兩個(gè)不同的request對(duì)象)
// session對(duì)象代表一次會(huì)話,(一次會(huì)話對(duì)應(yīng)一個(gè)session 對(duì)象)
// 獲取session,如何服務(wù)器當(dāng)中沒有 session 對(duì)象就會(huì)自動(dòng)創(chuàng)建一個(gè),
HttpSession session = request.getSession();
// 獲取到服務(wù)器端的 session ,如果沒有不會(huì)自動(dòng)創(chuàng)建 session 對(duì)象
//HttpSession session1 = request.getSession(false);
//session.setAttribute(); 將數(shù)據(jù)存儲(chǔ)到 session 會(huì)話當(dāng)中。
//session.getAttribute() 將數(shù)據(jù)從 session 會(huì)話當(dāng)中取出
// 將session 對(duì)象響應(yīng)到瀏覽器端
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("會(huì)話對(duì)象:" + session);
}
}
結(jié)果:
什么表示一個(gè)會(huì)話
粗略的可以認(rèn)為一個(gè):當(dāng)我們打開一個(gè)瀏覽器訪問一個(gè)A服務(wù)器的時(shí)候,如果這個(gè)瀏覽器不關(guān)閉的情況下,該瀏覽器發(fā)送的請(qǐng)求都是向 A服務(wù)器,那么哪個(gè)瀏覽器發(fā)送對(duì)于這個(gè)A服務(wù)器發(fā)送的所有的請(qǐng)求都可以理解為是一個(gè) session 會(huì)話。
我們也可以再精細(xì)一點(diǎn)的再說一下:就是我們?cè)诰〇|網(wǎng)站,A用戶登錄以后,在京東網(wǎng)站當(dāng)中的,進(jìn)行查詢商品,購買商品,添加商品購物車,等等,都是屬于該 A用戶專屬的一個(gè) session 的會(huì)話,當(dāng)我們?cè)僭诰〇|網(wǎng)站當(dāng)中,B用戶登錄以后,在京東網(wǎng)站當(dāng)中的,進(jìn)行查詢商品,購買商品,添加商品購物車,等等這些是 B用戶請(qǐng)求操作的都是專屬于一個(gè) session 會(huì)話。
為什么不使用request對(duì)象保存會(huì)話狀態(tài)?為什么不使用ServletContext對(duì)象保存會(huì)話狀態(tài)?
- request.setAttribute()存數(shù)據(jù),request.getAttribute()取數(shù)據(jù),ServletContext也有這個(gè)方法。request是請(qǐng)求域。ServletContext是應(yīng)用域。
- request是一次請(qǐng)求一個(gè)對(duì)象。
- ServletContext對(duì)象是服務(wù)器啟動(dòng)的時(shí)候創(chuàng)建,服務(wù)器關(guān)閉的時(shí)候銷毀,這個(gè)ServletContext對(duì)象只有一個(gè)。
- ServletContext對(duì)象的域太大。
- request請(qǐng)求域(HttpServletRequest)、session會(huì)話域(HttpSession)、application應(yīng)用域(ServletContext)
- 三個(gè)域之間的作用域的大小關(guān)系:request (請(qǐng)求域)< session(會(huì)話域) < application(應(yīng)用域) 。
4. session 的實(shí)現(xiàn)原理解釋
HttpSession session = request.getSession();
這行代碼很神奇。張三訪問的時(shí)候獲取的 session 對(duì)象就是張三專屬的。李四訪問的時(shí)候獲取的 session 對(duì)象就是李四專屬的。
這是如何做到的呢?我們可以舉一個(gè)有關(guān)于我們實(shí)際生活當(dāng)中的一個(gè)例子:
比如: 我們張三,李四都是在同一個(gè)大學(xué)的班級(jí)當(dāng)中,張三和李四上的都是同一個(gè)籃球課(體育課),當(dāng)他們上課的時(shí)候
,他們的體育老師帶來了(一筐籃球)(就是 session ),讓同學(xué)們自行挑選好自己的籃球,用于上籃球課。這時(shí)候我們的張三認(rèn)真的挑選到了一個(gè)籃球,并且試了試手感,感覺十分的不錯(cuò)。心里就有了一點(diǎn)小心思:就是想要,自己每次上籃球課的時(shí)候,都可以找到,并拿到這個(gè)手感不錯(cuò)的籃球。怎么實(shí)現(xiàn)這個(gè)想法呢?于是,張三同學(xué)就在,這個(gè)他手中的(手感不錯(cuò))籃球上做了一個(gè)標(biāo)記(SESSIONID=xxxxxx)。這個(gè)標(biāo)記只有張三自己知道是干什么的,其他同學(xué)都不知道。這樣當(dāng)下次以后的每一節(jié)籃球課,張三都可以根據(jù)自己所作的這個(gè)標(biāo)記,從眾多籃球當(dāng)中,找到這個(gè),自己標(biāo)記到的籃球了。
這個(gè)例子當(dāng)中的: 一筐籃球就可以比作是 : 服務(wù)器的當(dāng)中的 session 會(huì)話對(duì)象,而其中的 張三自己在籃球上作的標(biāo)記就可以比作是: SESSIONID=xxxxxx 是 session 對(duì)象的 ID 了。
session 生成的過程:
一個(gè) session 會(huì)話對(duì)象 對(duì)應(yīng)一個(gè) JSESSIONID=xxxxxx (就是一個(gè)標(biāo)記 session 會(huì)話對(duì)象的 ID (類似于一個(gè)人的身份證信息)是唯一的)。
服務(wù)器當(dāng)中是有一個(gè)類似于 Map 的一個(gè) session 列表。該 session 列表當(dāng)中存在兩樣?xùn)|西: key 對(duì)應(yīng)的是 JSESSIONID=xxxxxx (也就是 session 的ID的標(biāo)記) ,而 value 對(duì)應(yīng)的則是 session 對(duì)象。
key (session 的 ID) | value ( session 對(duì)象) |
---|---|
JSESSIONID=123 | session1 |
JSESSIONID=456 | session2 |
當(dāng)用戶第一次請(qǐng)求服務(wù)器的時(shí)候,服務(wù)器會(huì)為該用戶生成一個(gè) session 會(huì)話對(duì)象,同時(shí)服務(wù)器會(huì)將該 session 對(duì)應(yīng) JSESSIONID=123,也就是: sessionID 發(fā)送給客戶端??蛻舳藭?huì)接收到服務(wù)器發(fā)送過來的 JSESSIONID ,并存儲(chǔ)到 客戶端的緩存(Cookie) 當(dāng)中。同時(shí)需要注意的是: JSESSIONID=xxxxxx 這個(gè)是以Cookie的形式保存在瀏覽器的內(nèi)存中的。瀏覽器只要關(guān)閉。這個(gè)cookie就沒有了。(當(dāng)然這是默認(rèn)的情況下,你是可以自定義設(shè)置的。關(guān)于 Cookie 的內(nèi)容這里就不會(huì)說明了。)
舉例:具體代碼詳細(xì)如下:
package com.RainbowSea.session;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/session")
public class TestSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
HttpSession session = request.getSession();
//
response.setContentType("text/html;charSet=UTF-8");
PrintWriter out = response.getWriter();
//
out.println(" session對(duì)象: " + session);
}
}
如下是我們的瀏覽器(客戶端) 向服務(wù)器 第一次 發(fā)送請(qǐng)求(response) 的效果圖:如下:
第二次,我們的瀏覽器(客戶端)向服務(wù)器發(fā)送 第二次請(qǐng)求(response) ,因?yàn)槲覀優(yōu)g覽器(客戶端)第一次請(qǐng)求的時(shí)候,已經(jīng)將服務(wù)器響應(yīng)過來的 JSESSIONID 存儲(chǔ)到了,自己客戶端的 Cookie 當(dāng)中去了。所以,當(dāng)我們的客戶端再次向上一個(gè)服務(wù)器發(fā)送請(qǐng)求的時(shí)候,這是同屬于同一個(gè)會(huì)話的,所以我們的客戶端將第一次請(qǐng)求的時(shí)候,獲取到的 JSESSIONID 發(fā)送給 服務(wù)器,服務(wù)器根據(jù) JSESSIONID 查找session對(duì)象。 返回給客戶端,所以兩者之間的 session 對(duì)象的地址是一樣的,因?yàn)槭峭瑢儆谕粋€(gè)會(huì)話的。測(cè)試效果如下:
注意:我們的瀏覽器是遵循 HTTP 協(xié)議的,而 HTTP 協(xié)議是 無狀態(tài)的,導(dǎo)致我們的服務(wù)器無法知道瀏覽器關(guān)閉了,所以我們的 會(huì)話銷毀存在一種(延遲銷毀的機(jī)制:簡(jiǎn)單的說就是,當(dāng)一個(gè) session 會(huì)話,在一定的時(shí)間段內(nèi)沒有,任何的請(qǐng)求發(fā)發(fā)送了,服務(wù)器就會(huì)認(rèn)為該 sessoin 沒有用了,會(huì)自動(dòng)銷毀該 session 會(huì)話對(duì)象了),當(dāng)我們關(guān)閉瀏覽器,內(nèi)存消失,Cookie 消失,Cookie 消失了,那存在其中的 JSESSIONID (也就是 sessionID ) 自然也就消失了。而 JSESSIONID 消失了,我們的客戶端也就無法根據(jù)該 JSESSIONID 獲取到,訪問到 對(duì)應(yīng)的 session 對(duì)象了,當(dāng)?shù)竭_(dá)一定的時(shí)間段后,還是沒有任何客戶端訪問該 Session 會(huì)話,服務(wù)器就會(huì)自動(dòng)銷毀該 session 會(huì)話對(duì)象了。
關(guān)閉瀏覽器,重新發(fā)送請(qǐng)求,測(cè)試效果如下圖所示:
session對(duì)象的銷毀:
session 對(duì)象是什么時(shí)候銷毀:
瀏覽器關(guān)閉的時(shí)候,服務(wù)器是不知道的,服務(wù)器無法監(jiān)測(cè)到瀏覽器關(guān)閉了(HTTP協(xié)議是無狀態(tài)協(xié)議),所以 session 的銷毀要依靠 session 超時(shí)機(jī)制,
但也有一種可能,系統(tǒng)提供了 “安全退出”,用戶可以點(diǎn)擊這個(gè)按鈕,這樣服務(wù)器就知道你退出了,然后服務(wù)器會(huì)自動(dòng)銷毀 session 對(duì)象。
- 第一種: 手動(dòng)銷毀
// 銷毀 session 對(duì)象的 session.invalidate();
- 第二種:自動(dòng)銷毀(超時(shí)銷毀)
為什么關(guān)閉瀏覽器,會(huì)話結(jié)束?
關(guān)閉瀏覽器之后,瀏覽器中保存的 JSESSIONID (也就是 session 的ID)消失,下次重新打開瀏覽器之后,
瀏覽器緩存中沒有這個(gè) session的ID,自然找不到 服務(wù)器中對(duì)應(yīng)的 session 對(duì)象,session 對(duì)象找不到,等同于會(huì)話結(jié)束。(超時(shí)銷毀,當(dāng)一個(gè) session 一段時(shí)間內(nèi)沒有,被訪問了,就會(huì)自動(dòng)被服務(wù)器銷毀,這里我們的 JSESSIONID 都沒有了,我們就無法找到對(duì)應(yīng) session 的對(duì)象,無法找到 session 對(duì)象,就更無法訪問了。)
session 超時(shí)銷毀機(jī)制的設(shè)置的時(shí)間點(diǎn),默認(rèn)是 Tomcat apache-tomcat-10.0.12\conf\web.xml的
web.xml
配置當(dāng)中,默認(rèn)配置為了 30 分鐘
<!-- ==================== Default Session Configuration ================= --> <!-- You can set the default session timeout (in minutes) for all newly --> <!-- created sessions by modifying the value below. --> <session-config> <session-timeout>30</session-timeout> </session-config>
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-sJQQVVaF-1682775113687)(E:\博客\javaWed博客庫\image-20230424221802452.png)]
當(dāng)然,這個(gè) session 超時(shí)銷毀的時(shí)間點(diǎn),我們也是可以設(shè)置的。
我們可以根據(jù)自己的需要設(shè)置,比如:如果是一個(gè)銀行的安全信息的話,可以設(shè)置為 1~5 分鐘。如果是一個(gè)長(zhǎng)久使用的話可以設(shè)置為 24 小時(shí),7天等等。根據(jù)實(shí)際業(yè)務(wù)需要靈活的設(shè)置。
重點(diǎn):如下是 session 的生成,銷毀的原理圖示:
5. 補(bǔ)充: Cookie禁用了,session還能找到嗎 ?
cookie禁用是什么意思?服務(wù)器正常發(fā)送cookie給瀏覽器,但是瀏覽器不要了。拒收了。并不是服務(wù)器不發(fā)了。
如下是: Google Chrome 瀏覽器禁用 Cookie 的設(shè)置:
當(dāng)我們禁用了瀏覽器的 Cookie 設(shè)置,再次訪問我們的 Servlet 服務(wù)器的效果如下:
下面這個(gè)是 Firefox火狐瀏覽器的禁用 Cookie 的設(shè)置。
結(jié)論:當(dāng)瀏覽器禁用了Cookie 緩存功能,服務(wù)器正常發(fā)送cookie信息(包括了 JSESSIONID 信息)給瀏覽器,但是瀏覽器不要了。拒收了,并不是服務(wù)器不發(fā)了。所以導(dǎo)致的結(jié)果就是:客戶端不會(huì)發(fā)送給服務(wù)器 JSESSIONID信息了,找不到了,每一次請(qǐng)求都會(huì)獲取到新的session對(duì)象。
問題:cookie禁用了,session機(jī)制還能實(shí)現(xiàn)嗎?
可以,需要使用 URL 重寫機(jī)制。
如下:演示:當(dāng)我們?cè)L問服務(wù)器時(shí),通過瀏覽器的 檢查功能中的 ——> 網(wǎng)絡(luò)(NetWork) 當(dāng)中的第一次請(qǐng)求服務(wù)器,服務(wù)器響應(yīng)給客戶端的 JSESSIONID 的信息會(huì)顯示在其中的:response headers (請(qǐng)求頭當(dāng)中 )。
將其中的
jsessionid=19D1C99560DCBF84839FA43D58F56E16
拼接到我們?cè)L問的 URL當(dāng)中,中間使用;
分號(hào)隔開。如下:需要注意的是,將其中的 JSESSIONID 寫成小寫的:jsessionid
http://127.0.0.1:8080/servlet14/session;jsessionid=F247C2C5CBE489F45383D116224F071B
原理:是雖然我們?yōu)g覽器沒有保存住服務(wù)器響應(yīng)過來的JSESSIONID信息,但是我們手動(dòng)將其中的SESSIOND給記住了,并通過地址欄的方式,get的方式發(fā)送給了服務(wù)器,服務(wù)器就會(huì)幫我們?nèi)ession列表當(dāng)中找到該對(duì)過的JSESSIONID的
session對(duì)象,而不是新建esssion對(duì)象了。
URL重寫機(jī)制會(huì)提高開發(fā)者的成本。開發(fā)人員在編寫任何請(qǐng)求路徑的時(shí)候,后面都要添加一個(gè)sessionid,給開發(fā)帶來了很大的難度,很大的成本。所以大部分的網(wǎng)站都是這樣設(shè)計(jì)的:你要是禁用cookie,你就別用了。
怎么理解這個(gè): 你要是禁用了 Cookie 緩存機(jī)制,你就別用了。就是說,如果你把 Cookie 禁用了一些網(wǎng)站你可能打不開來,或者說無法顯示全部?jī)?nèi)容信息。當(dāng)你開始這個(gè)設(shè)置 禁用Cookie 都會(huì)有一些提示的信息給到你的。比如:
。如下當(dāng)我們把 Firefox火狐瀏覽器的禁用 Cookie 打開,訪問
- 京東網(wǎng)站:https://www.jd.com/
- 訪問淘寶:https://www.taobao.com/
- 訪問唯品會(huì):https://www.vip.com/
- 訪問12306 網(wǎng)站:https://www.12306.cn/index/
6. 總結(jié)一下到目前位置我們所了解的域?qū)ο螅?/h2>
- request(對(duì)應(yīng)的類名:HttpServletRequest)請(qǐng)求域(請(qǐng)求級(jí)別的)
- session(對(duì)應(yīng)的類名:HttpSession)會(huì)話域(用戶級(jí)別的)
- application(對(duì)應(yīng)的類名:ServletContext)應(yīng)用域(項(xiàng)目級(jí)別的,所有用戶共享的。)
- 這三個(gè)域?qū)ο蟮拇笮£P(guān)系:request < session < application
- 他們?nèi)齻€(gè)域?qū)ο蠖加幸韵氯齻€(gè)公共的方法:
- setAttribute(向域當(dāng)中綁定數(shù)據(jù))
- getAttribute(從域當(dāng)中獲取數(shù)據(jù))
- removeAttribute(刪除域當(dāng)中的數(shù)據(jù))
- 使用原則:盡量使用小的域。
7. oa 項(xiàng)目的優(yōu)化體驗(yàn):使用上 session 會(huì)話機(jī)制:
- setAttribute(向域當(dāng)中綁定數(shù)據(jù))
- getAttribute(從域當(dāng)中獲取數(shù)據(jù))
- removeAttribute(刪除域當(dāng)中的數(shù)據(jù))
閱讀如下內(nèi)容,大家可以先移步至: Servlet注解的使用,簡(jiǎn)化配置 以及,使用模板方法設(shè)計(jì)模式優(yōu)化oa項(xiàng)目_ChinaRainbowSea的博客-CSDN博客看看有助于閱讀理解。
session掌握之后,我們?cè)趺唇鉀Qoa項(xiàng)目中的登錄問題:就是我們的登錄頁面是一個(gè)擺設(shè),當(dāng)用戶沒有登錄的情況下,可以直接通過在地址欄上輸入 URL 可以訪問到對(duì)應(yīng)的資源信息。
這里我們可以使用: session 會(huì)話機(jī)制,讓登錄起作用:就是如果用戶直接通過在地址欄上輸入 URL 可以訪問到對(duì)應(yīng)的資源信息的時(shí)候,判斷用戶是否登錄過,如果登錄過,則可以直接訪問。如果沒有登錄過就跳轉(zhuǎn)到登錄頁面,進(jìn)行一個(gè)正確的登錄成功的操作,才可以訪問。同時(shí)設(shè)置一個(gè)安全退出系統(tǒng),銷毀 session 對(duì)象的按鈕設(shè)置。
登錄成功之后,可以將用戶的登錄信息存儲(chǔ)到session當(dāng)中。也就是說session中如果有用戶的信息就代表用戶登錄成功了。session中沒有用戶信息,表示用戶沒有登錄過。則跳轉(zhuǎn)到登錄頁面。
優(yōu)化源碼如下:
首先是登錄頁面的優(yōu)化:當(dāng)用戶登錄成功,將用戶的登錄信息存儲(chǔ)到session當(dāng)中(這里我們存儲(chǔ)到用戶的用戶名信息。)
核心優(yōu)化代碼:
// 登錄成功與否
if (success) {
// 成功,跳轉(zhuǎn)到用戶列表頁面
// 這里使用重定向(沒有資源的共享):重定向需要加/項(xiàng)目名 +
// 獲取session 對(duì)象(這里的要求是: 必須獲取到 session ,沒有session 也要新建一個(gè) session 對(duì)象)
// 注意:我們下面的這個(gè)會(huì)話是不能刪除的,因?yàn)樯厦嫖覀冸m然通過 welcome Servlet 進(jìn)行了一個(gè)會(huì)話
// 但是 welcome 當(dāng)中是當(dāng)我們cookie 當(dāng)中存在并且用戶名和密碼正確的時(shí)候才會(huì)進(jìn)行一個(gè) session 的
HttpSession session = request.getSession(); // 服務(wù)器當(dāng)中沒有 session 會(huì)話域自動(dòng)創(chuàng)建
session.setAttribute("username", username); // 將用戶名存儲(chǔ)到 session 會(huì)話域當(dāng)中
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
// 失敗,跳轉(zhuǎn)到失敗頁面
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
全部的代碼:
package com.RainbowSea.servlet;
import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@WebServlet({"/user/login", "/user/exit"})
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// 獲取到瀏覽器地址欄上的URL路徑
String servletPath = request.getServletPath();
if ("/user/login".equals(servletPath)) {
doLogin(request, response);
} else if ("/user/exit".equals(servletPath)) {
doExit(request, response);
}
}
private void doExit(HttpServletRequest request, HttpServletResponse response) throws IOException {
}
protected void doLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// 一個(gè)用戶登錄驗(yàn)證的方式:驗(yàn)證用戶名和密碼是否正確
// 獲取用戶名和密碼
// 前端提交是數(shù)據(jù)是:username=111&password=fads
// 注意:post 提交的數(shù)據(jù)是在請(qǐng)求體當(dāng)中,而get提交的數(shù)據(jù)是在請(qǐng)求行當(dāng)中
boolean success = false; // 標(biāo)識(shí)登錄成功
String username = request.getParameter("username");
String password = request.getParameter("password");
String exempt = request.getParameter("exempt");
// 連接數(shù)據(jù)庫驗(yàn)證用戶名和密碼
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 1. 獲取連接,注冊(cè)驅(qū)動(dòng)
connection = DBUtil.getConnection();
// 2. 獲取操作數(shù)據(jù)對(duì)象,預(yù)編譯sql語句, ? 占位符不要加,“”,'' 單雙引號(hào),成了字符串了,無法識(shí)別成占位符了。
String sql = "select username,password from t_user where username = ? and password = ?";
preparedStatement = connection.prepareStatement(sql);
// 3. 填充占位符,真正執(zhí)行sql語句
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
resultSet = preparedStatement.executeQuery();
// 4. 處理查詢結(jié)果集
// 只有一條結(jié)果集
if (resultSet.next()) {
// 登錄成功
success = true;
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5. 關(guān)閉資源,最后使用的最先關(guān)閉,
DBUtil.close(connection, preparedStatement, resultSet);
}
// 登錄成功與否
if (success) {
// 成功,跳轉(zhuǎn)到用戶列表頁面
// 這里使用重定向(沒有資源的共享):重定向需要加/項(xiàng)目名 +
// 獲取session 對(duì)象(這里的要求是: 必須獲取到 session ,沒有session 也要新建一個(gè) session 對(duì)象)
// 注意:我們下面的這個(gè)會(huì)話是不能刪除的,因?yàn)樯厦嫖覀冸m然通過 welcome Servlet 進(jìn)行了一個(gè)會(huì)話
// 但是 welcome 當(dāng)中是當(dāng)我們cookie 當(dāng)中存在并且用戶名和密碼正確的時(shí)候才會(huì)進(jìn)行一個(gè) session 的
HttpSession session = request.getSession(); // 服務(wù)器當(dāng)中沒有 session 會(huì)話域自動(dòng)創(chuàng)建
session.setAttribute("username", username); // 將用戶名存儲(chǔ)到 session 會(huì)話域當(dāng)中
response.sendRedirect(request.getContextPath() + "/dept/list");
} else {
// 失敗,跳轉(zhuǎn)到失敗頁面
response.sendRedirect(request.getContextPath() + "/error.jsp");
}
}
}
其次是當(dāng)用戶想要直接通過 URL訪問的時(shí)候,判斷用戶是否登錄成功過,登錄成功過可以訪問,沒有登錄成功過無法訪問:
思路是:
我們通過 session 會(huì)話機(jī)制,判斷用戶是否登錄過,如果用戶沒有登錄就想要訪問 到其信息,不可以,因?yàn)槲覀冞@里判斷了一次是否登錄過,只有登錄入過了,才會(huì)將中登錄到用戶名為 “username” 的信息存儲(chǔ)到 session 會(huì)話當(dāng)中,如果沒有的話是查詢不到的,返回的是 null。需要注意的一點(diǎn)就是,我們的jsp 當(dāng)中的內(nèi)置對(duì)象,是會(huì)自動(dòng)創(chuàng)建一個(gè) session 會(huì)話對(duì)象的(所以就會(huì)導(dǎo)致,就算我們沒有登錄成功 ,session 對(duì)象也是不為空的,因?yàn)镴SP創(chuàng)建了 session 對(duì)象,我們可以通過JSP 指令禁止 JSP 生成 session 內(nèi)置對(duì)象
<%@page session = false %>
,需要所有會(huì)被訪問,生成的 Jsp 文件都需要設(shè)置該指令。這里 所謂的禁用了就是,對(duì)應(yīng)的訪問生成的 xxx_jsp.java) 當(dāng)中不會(huì)翻譯生成其中內(nèi)置的 session 對(duì)象),但是因?yàn)檫@里我們進(jìn)行了一個(gè) 雙重的判斷機(jī)制。if(session != null && session.getAttribute("username") != null) // 雙重的判斷,一個(gè)是 session 會(huì)話域要存在,其次是 會(huì)話域當(dāng)中存儲(chǔ)了名為 "username" 的信息,可以用戶登錄的信息可以從 session 找到,如果找不到 ,返回 null ,找到不為 null 。這樣就解決了 JSP 內(nèi)置session 對(duì)象的沒有登錄 session 不為 null 的影響了。
需要注意一點(diǎn)的就是:這里我們要使用
HttpSession session = request.getSession(false)
HttpSession session = request.getSession(false); // 獲取到服務(wù)器當(dāng)中的session ,沒有不會(huì)創(chuàng)建的, // session 是用戶登錄成功才創(chuàng)建的,其他情況不要?jiǎng)?chuàng)建 session 會(huì)話對(duì)象。
核心代碼:
// 可以使用模糊查詢 @WebServlet("/dept/*")
@WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"})
public class DeptServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
String servletPath = request.getServletPath(); // 獲取到瀏覽器當(dāng)中的uri
// 獲取session 這個(gè) session 是不不需要新建的
// 只是獲取當(dāng)前session ,獲取不到這返回null,
HttpSession session = request.getSession(false); // 獲取到服務(wù)器當(dāng)中的session ,沒有不會(huì)創(chuàng)建的
/**
* 說明這里我們通過 session 會(huì)話機(jī)制,判斷用戶是否登錄過,如果用戶沒有登錄就想要訪問
* 到其信息,不可以,因?yàn)槲覀冞@里判斷了一次是否登錄過,只有登錄入過了,才會(huì)將中登錄到
* 用戶名為 “username” 的信息存儲(chǔ)到 session 會(huì)話當(dāng)中,如果沒有的話是查詢不到的,返回的是 null
* 需要注意的一點(diǎn)就是,我們的jsp 當(dāng)中的內(nèi)置對(duì)象,是會(huì)自動(dòng)創(chuàng)建一個(gè) session 會(huì)話對(duì)象的,但是
* 因?yàn)檫@里我們進(jìn)行了一個(gè) 雙重的判斷機(jī)制。注意:需要先將對(duì)應(yīng)的 xx_jsp.java 生成才行。同時(shí)
* 使用 <%@page session = false %> 指令的話,需要所有會(huì)被訪問,生成的 Jsp 文件都需要設(shè)置。
*
* jakarta.servlet.http.HttpSession session = null;
* session = pageContext.getSession();
*/
if(session != null && session.getAttribute("username") != null) {
// 雙重的判斷,一個(gè)是 session 會(huì)話域要存在,其次是 會(huì)話域當(dāng)中存儲(chǔ)了名為 "username" 的信息
if ("/dept/list".equals(servletPath)) {
doList(request, response);
} else if ("/dept/detail".equals(servletPath)) {
doDetail(request, response);
} else if ("/dept/delete".equals(servletPath)) {
doElete(request,response);
} else if("/dept/save".equals(servletPath)) {
doSave(request,response);
} else if("/dept/modify".equals(servletPath)) {
doModify(request,response);
}
} else {
response.sendRedirect(request.getContextPath()); // 訪問的web 站點(diǎn)的根即可,自動(dòng)找到的是名為 index.jsp
}
}
}
最后就是:用戶點(diǎn)擊安全退出系統(tǒng),銷毀 session 對(duì)象的實(shí)現(xiàn)了。
當(dāng)我們點(diǎn)擊 安全退出,手動(dòng)將 session 會(huì)話對(duì)象銷毀了。就需要重新登錄了。只有重新登錄,建立新的登錄成功的 session 會(huì)話信息,才能再次通過URL訪問。
核心代碼如下:
session.invalidate(); // 銷毀 session 對(duì)象。
/**
* 用戶手動(dòng)點(diǎn)擊安全退出,銷毀 session 對(duì)象
* @param request
* @param response
* @throws IOException
*/
private void doExit(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 獲取到客戶端發(fā)送過來的 sessoin
HttpSession session = request.getSession();
if (session != null) {
// 手動(dòng)銷毀 session 對(duì)象
// 注意:會(huì)話銷毀的了,自然需要重寫登錄了,沒有登錄過,無法進(jìn)行一個(gè)路徑的訪問的
session.invalidate();
// 跳轉(zhuǎn)會(huì)登錄的頁面
response.sendRedirect(request.getContextPath()); // 項(xiàng)目名路徑默認(rèn)就是訪問的index.html 的歡迎頁面
}
}
全部具體代碼:
package com.RainbowSea.servlet;
import com.RainbowSea.DBUtil.DBUtil;
import com.RainbowSea.bean.Dept;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
// 可以使用模糊查詢 @WebServlet("/dept/*")
@WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"})
public class DeptServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
String servletPath = request.getServletPath(); // 獲取到瀏覽器當(dāng)中的uri
// 獲取session 這個(gè) session 是不不需要新建的
// 只是獲取當(dāng)前session ,獲取不到這返回null,
HttpSession session = request.getSession(false); // 獲取到服務(wù)器當(dāng)中的session ,沒有不會(huì)創(chuàng)建的
/**
* 說明這里我們通過 session 會(huì)話機(jī)制,判斷用戶是否登錄過,如果用戶沒有登錄就想要訪問
* 到其信息,不可以,因?yàn)槲覀冞@里判斷了一次是否登錄過,只有登錄入過了,才會(huì)將中登錄到
* 用戶名為 “username” 的信息存儲(chǔ)到 session 會(huì)話當(dāng)中,如果沒有的話是查詢不到的,返回的是 null
* 需要注意的一點(diǎn)就是,我們的jsp 當(dāng)中的內(nèi)置對(duì)象,是會(huì)自動(dòng)創(chuàng)建一個(gè) session 會(huì)話對(duì)象的,但是
* 因?yàn)檫@里我們進(jìn)行了一個(gè) 雙重的判斷機(jī)制。注意:需要先將對(duì)應(yīng)的 xx_jsp.java 生成才行。同時(shí)
* 使用 <%@page session = false %> 指令的話,需要所有會(huì)被訪問,生成的 Jsp 文件都需要設(shè)置。
*
* jakarta.servlet.http.HttpSession session = null;
* session = pageContext.getSession();
*/
if(session != null && session.getAttribute("username") != null) {
// 雙重的判斷,一個(gè)是 session 會(huì)話域要存在,其次是 會(huì)話域當(dāng)中存儲(chǔ)了名為 "username" 的信息
if ("/dept/list".equals(servletPath)) {
doList(request, response);
} else if ("/dept/detail".equals(servletPath)) {
doDetail(request, response);
} else if ("/dept/delete".equals(servletPath)) {
doElete(request,response);
} else if("/dept/save".equals(servletPath)) {
doSave(request,response);
} else if("/dept/modify".equals(servletPath)) {
doModify(request,response);
}
} else {
response.sendRedirect(request.getContextPath()); // 訪問的web 站點(diǎn)的根即可,自動(dòng)找到的是名為 index.jsp
}
}
/**
* 修改部門信息
*
* @param request
* @param response
*/
private void doModify(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding("UTF-8"); // 設(shè)置獲取的的信息的編碼集
Connection connection = null;
PreparedStatement preparedStatement = null;
// 影響數(shù)據(jù)庫的行數(shù)
int count = 0;
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
try {
// 1. 注冊(cè)驅(qū)動(dòng),連接數(shù)據(jù)庫
connection = DBUtil.getConnection();
// 2. 獲取到操作數(shù)據(jù)庫的對(duì)象,預(yù)編譯sql語句,sql測(cè)試
String sql = "update dept set dname = ?,loc = ? where depton = ?";
preparedStatement = connection.prepareStatement(sql);
// 3. 填充占位符,真正執(zhí)行sql語句
// 從下標(biāo) 1開始
preparedStatement.setString(1, dname);
preparedStatement.setString(2, loc);
preparedStatement.setString(3, deptno);
count = preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 4. 釋放資源,最后使用的優(yōu)先被釋放
DBUtil.close(connection, preparedStatement, null);
}
if (count == 1) {
// 更新成功
// 跳轉(zhuǎn)到部門列表頁面(部門列表表面是通過java程序動(dòng)態(tài)生成的,所以還需要再次執(zhí)行另一個(gè)Servlet)
// 轉(zhuǎn)發(fā)是服務(wù)器內(nèi)部的操作,“/” 不要加項(xiàng)目名
// request.getRequestDispatcher("/dept/list/").forward(request,response);
// 優(yōu)化使用重定向,自發(fā)前端(需要指明項(xiàng)目名)
response.sendRedirect(request.getContextPath() + "/dept/list");
}
}
/**
* 保存部門信息
*
* @param request
* @param response
*/
private void doSave(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding("UTF-8");
// 獲取到前端的數(shù)據(jù),建議 name 使用復(fù)制
String deptno = request.getParameter("deptno");
String dname = request.getParameter("dname");
String loc = request.getParameter("loc");
// 連接數(shù)據(jù)庫,添加數(shù)據(jù)
Connection connection = null;
PreparedStatement preparedStatement = null;
// 影響數(shù)據(jù)庫的行數(shù)
int count = 0;
try {
// 1. 注冊(cè)驅(qū)動(dòng),連接數(shù)據(jù)庫
connection = DBUtil.getConnection();
// 2. 獲取操作數(shù)據(jù)庫對(duì)象,預(yù)編譯sql語句,Sql測(cè)試
String sql = "insert into dept(depton,dname,loc) values(?,?,?)";
preparedStatement = connection.prepareStatement(sql);
// 3. 填充占位符, 真正執(zhí)行sql語句,
// 注意: 占位符的填充是從 1 開始的,基本上數(shù)據(jù)庫相關(guān)的起始下標(biāo)索引都是從 1下標(biāo)開始的
preparedStatement.setString(1, deptno);
preparedStatement.setString(2, dname);
preparedStatement.setString(3, loc);
// 返回影響數(shù)據(jù)庫的行數(shù)
count = preparedStatement.executeUpdate();
// 5.釋放資源
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(connection, preparedStatement, null);
}
// 保存成功,返回部門列表頁面
if (count == 1) {
// 這里應(yīng)該使用,重定向
// 這里用的轉(zhuǎn)發(fā),是服務(wù)器內(nèi)部的,不要加項(xiàng)目名
//request.getRequestDispatcher("/dept/list/").forward(request, response);
// 重定向
response.sendRedirect(request.getContextPath() + "/dept/list");
}
}
/**
* 通過部門刪除部門
*
* @param request
* @param response
*/
private void doElete(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding("UTF-8"); // 設(shè)置獲取的的信息的編碼集
// 獲取到發(fā)送數(shù)據(jù)
String deptno = request.getParameter("deptno");
/*
根據(jù)部門編號(hào)刪除信息,
刪除成功,跳轉(zhuǎn)回原來的部門列表頁面
刪除失敗,跳轉(zhuǎn)刪除失敗的頁面
*/
Connection connection = null;
PreparedStatement preparedStatement = null;
// 記錄刪除數(shù)據(jù)庫的行數(shù)
int count = 0;
// 連接數(shù)據(jù)庫進(jìn)行刪除操作
try {
// 1.注冊(cè)驅(qū)動(dòng),連接數(shù)據(jù)庫
connection = DBUtil.getConnection();
// 開啟事務(wù)(取消自動(dòng)提交機(jī)制),實(shí)現(xiàn)可回滾
connection.setAutoCommit(false);
// 2. 預(yù)編譯sql語句,sql測(cè)試
String sql = "delete from dept where depton = ?"; // ? 占位符
preparedStatement = connection.prepareStatement(sql);
// 3. 填充占位符,真正的執(zhí)行sql語句
preparedStatement.setString(1, deptno);
// 返回影響數(shù)據(jù)庫的行數(shù)
count = preparedStatement.executeUpdate();
connection.commit(); // 手動(dòng)提交數(shù)據(jù)
} catch (SQLException e) {
// 遇到異?;貪L
if (connection != null) {
try {
// 事務(wù)的回滾
connection.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
throw new RuntimeException(e);
} finally {
// 4. 釋放資源
// 因?yàn)檫@里是刪除數(shù)據(jù),沒有查詢操作,所以 沒有 ResultSet 可以傳null
DBUtil.close(connection, preparedStatement, null);
}
if (count == 1) {
// 刪除成功
// 仍然跳轉(zhuǎn)到部門列表頁面
// 部門列表頁面的顯示需要執(zhí)行另外一個(gè)Servlet,怎么辦,可以使用跳轉(zhuǎn),不過這里最后是使用重定向
// 注意:轉(zhuǎn)發(fā)是在服務(wù)器間的,所以不要加“項(xiàng)目名” 而是 / + web.xml 映射的路徑即可
//request.getRequestDispatcher("/dept/list/").forward(request,response);
// 優(yōu)化:使用重定向機(jī)制 注意: 重定向是自發(fā)到前端的地址欄上的,前端所以需要指明項(xiàng)目名
// 注意: request.getContextPath() 返回的根路徑是,包含了 "/" 的
response.sendRedirect(request.getContextPath() + "/dept/list");
}
}
/**
* 通過部門編號(hào),查詢部門的詳情
*
* @param request
* @param response
*/
private void doDetail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // 設(shè)置獲取的的信息的編碼集
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 獲取到部門編號(hào)
String dno = request.getParameter("dno");
Dept dept = new Dept();
// 獲取到部門編號(hào),獲取部門信息,將部門信息收集好,然后跳轉(zhuǎn)到JSP做頁面展示
try {
// 2. 連接數(shù)據(jù)庫,根據(jù)部門編號(hào)查詢數(shù)據(jù)庫
// 1.注冊(cè)驅(qū)動(dòng),連接數(shù)據(jù)庫
connection = DBUtil.getConnection();
// 2. 預(yù)編譯SQL語句,sql要測(cè)試
String sql = "select dname,loc from dept where depton = ?"; // ? 占位符
preparedStatement = connection.prepareStatement(sql);
// 3. 填充占位符,真正執(zhí)行sql語句
preparedStatement.setString(1, dno);
resultSet = preparedStatement.executeQuery();
// 4. 處理查詢結(jié)果集
while (resultSet.next()) {
String dname = resultSet.getString("dname");
String loc = resultSet.getString("loc");
// 封裝對(duì)象(建議使用咖啡豆,因?yàn)橹挥幸粋€(gè)對(duì)象)
dept.setDeptno(dno);
dept.setDname(dname);
dept.setLoc(loc);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5. 釋放資源
DBUtil.close(connection, preparedStatement, resultSet);
}
// 這個(gè)咖啡豆只有一個(gè),所以不需要袋子,只需要將這個(gè)咖啡豆放到request請(qǐng)求域當(dāng)中,
// 用于對(duì)應(yīng)的 jsp顯示
request.setAttribute("dept", dept);
//String sign = request.getParameter("f");
/*if("m".equals(sign)) {
// 轉(zhuǎn)發(fā):多個(gè)請(qǐng)求為一個(gè)請(qǐng)求(地址欄不會(huì)發(fā)生改變)
// 注意: 該路徑默認(rèn)是從 web 開始找的 / 表示 web
// 轉(zhuǎn)發(fā)到修改頁面
request.getRequestDispatcher("/edit.jsp").forward(request,response);
} else if("d".equals(sign)) {
// 跳轉(zhuǎn)到詳情頁面
request.getRequestDispatcher("/detail.jsp").forward(request,response);
}*/
// 或者優(yōu)化
// 注意 無論是轉(zhuǎn)發(fā)還是重定向都是從 “/” 開始的
// request.getParameter()拿到的是 f=edit,還是f=detail 就是跳轉(zhuǎn)到的哪個(gè)頁面
//<a href="<%=request.getContextPath()%>/dept/detail?f=edit&dno=<%=dept.getDeptno()%>">修改</a>
//<a href="<%=request.getContextPath()%>/dept/detail?f=detail&dno=<%=dept.getDeptno()%>">詳情</a>
String forward = "/" + request.getParameter("f") + ".jsp";
request.getRequestDispatcher(forward).forward(request, response);
}
/**
* 連接數(shù)據(jù)庫,查詢所有的部門信息,將部門信息收集好,然后跳轉(zhuǎn)到JSP頁面展示
*
* @param request
* @param response
*/
private void doList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // 設(shè)置獲取的的信息的編碼集
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 創(chuàng)建一個(gè)集合List 存儲(chǔ)查詢到的信息
List<Dept> depts = new ArrayList<Dept>();
try {
// 連接數(shù)據(jù)庫,查詢所有部門:
// 1. 注冊(cè)驅(qū)動(dòng),獲取連接
connection = DBUtil.getConnection();
// 2. 獲取操作數(shù)據(jù)庫對(duì)象,預(yù)編譯sql語句
String sql = "select depton as det,dname,loc from dept"; // 在mysql中測(cè)試一下是否正確
preparedStatement = connection.prepareStatement(sql);
// 3. 執(zhí)行sql語句
resultSet = preparedStatement.executeQuery();
// 4. 處理查詢結(jié)果集
while (resultSet.next()) {
String det = resultSet.getString("det"); // 有別名要使用別名
String dname = resultSet.getString("dname");
String loc = resultSet.getString("loc");
Dept dept = new Dept(det, dname, loc);
// 將部門對(duì)象放到List集合當(dāng)中
depts.add(dept);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 5. 關(guān)閉資源
DBUtil.close(connection, preparedStatement, resultSet);
}
// 查詢到數(shù)據(jù),將數(shù)據(jù)提交給 list.jsp 顯示數(shù)據(jù)
// 將集合存儲(chǔ)的數(shù)據(jù)放到請(qǐng)求域當(dāng)中,用于其他Servlet 使用 jsp 也是Servelt
request.setAttribute("depList", depts);
// 轉(zhuǎn)發(fā)(注意不要重定向),重定向無法共用 request 請(qǐng)求域當(dāng)中的數(shù)據(jù)
// 轉(zhuǎn)發(fā)路徑,/ 默認(rèn)是從 web 目錄開始找的
request.getRequestDispatcher("/list.jsp").forward(request, response);
}
}
用戶界面的優(yōu)化:顯示 登錄的用戶名:(該用戶名信息,從 存儲(chǔ)到 session 會(huì)話對(duì)象當(dāng)中,獲取到的。)
核心代碼如下:
需要注意的點(diǎn)就是:這里我們使用的是 JSP 內(nèi)置的 session 對(duì)象,所以在這個(gè) JSP頁面當(dāng),你不可以把 session 禁用了。
不要設(shè)置這個(gè)禁用 session 的指令: <%@page session = false %>
優(yōu)化演示:
8. 總結(jié):
- session 會(huì)話用戶場(chǎng)景:在Web應(yīng)用程序中,我們經(jīng)常要跟蹤用戶身份。當(dāng)一個(gè)用戶登錄成功后,如果他繼續(xù)訪問其他頁面,Web程序如何才能識(shí)別出該用戶身份?
- session對(duì)象最主要的作用是:保存會(huì)話狀態(tài)。
- 為什么要保存會(huì)話狀態(tài):因?yàn)镠TTP協(xié)議是一種無狀態(tài)協(xié)議。
- 無狀態(tài):
- 優(yōu)點(diǎn):這樣服務(wù)器壓力小。
- 缺點(diǎn):服務(wù)器無法知道客戶端的狀態(tài)(是關(guān)閉的狀態(tài),還是開啟的狀態(tài))
- 無狀態(tài):
- 一個(gè) session 會(huì)話當(dāng)中包含多次請(qǐng)求(一次會(huì)話對(duì)應(yīng)N次請(qǐng)求。)
- session 對(duì)象是用服務(wù)器端生成的,所以這里是通過 request 請(qǐng)求的方式向服務(wù)器獲取到一個(gè) session 會(huì)話對(duì)象
- session 的生成,銷毀,傳遞的原理機(jī)制:
- 簡(jiǎn)單的來說吧 ,session 就是一個(gè)標(biāo)記,通過標(biāo)記 JSESSIONID 獲取到同一個(gè) session 對(duì)象,保證你對(duì)應(yīng)的操作是同一個(gè)用戶。
- Cookie禁用了,session還能找到嗎 ? 可以,使用 URL重寫機(jī)制。
- 實(shí)現(xiàn)用戶登錄,通過 session 會(huì)話機(jī)制(保存用戶登錄信息),實(shí)現(xiàn)用戶登錄成功,可以通過 URL 直接訪問資源,沒有登錄/登錄失敗,則無法直接通過 URL 訪問資源。
9. 最后:
限于自身水平,其中存在的錯(cuò)誤,希望大家給予指教,韓信點(diǎn)兵——多多益善,謝謝大家,江湖再見,后悔有期文章來源:http://www.zghlxwxcb.cn/news/detail-429298.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-429298.html
到了這里,關(guān)于B/S結(jié)構(gòu)系統(tǒng)的會(huì)話機(jī)制(session)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!