本篇文章為大家展示了使用SpringSession怎么實(shí)現(xiàn)請(qǐng)求與響應(yīng)重寫,內(nèi)容簡明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
成都創(chuàng)新互聯(lián)主營貢山網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,成都APP應(yīng)用開發(fā),貢山h5微信小程序定制開發(fā)搭建,貢山網(wǎng)站營銷推廣歡迎貢山等地區(qū)企業(yè)咨詢
1、請(qǐng)求重寫
SpringSession 中對(duì)于請(qǐng)求重寫,在能力上主要體現(xiàn)在存儲(chǔ)方面,也就是 getSession 方法上。在 SessionRepositoryFilter 這個(gè)類中,是通過內(nèi)部類的方式實(shí)現(xiàn)了對(duì) HttpServletRequset 和 HttpServletResponse 的擴(kuò)展。
1.1 HttpServletRequset 擴(kuò)展實(shí)現(xiàn)
private final class SessionRepositoryRequestWrapper
extends HttpServletRequestWrapper {
// HttpServletResponse 實(shí)例
private final HttpServletResponse response;
// ServletContext 實(shí)例
private final ServletContext servletContext;
// requestedSession session對(duì)象
private S requestedSession;
// 是否緩存 session
private boolean requestedSessionCached;
// sessionId
private String requestedSessionId;
// sessionId 是否有效
private Boolean requestedSessionIdValid;
// sessionId 是否失效
private boolean requestedSessionInvalidated;
// 省略方法
}1.2 構(gòu)造方法
private SessionRepositoryRequestWrapper(HttpServletRequest request,
HttpServletResponse response, ServletContext servletContext) {
super(request);
this.response = response;
this.servletContext = servletContext;
}構(gòu)造方法里面將 HttpServletRequest 、 HttpServletResponse 以及 ServletContext 實(shí)例傳遞進(jìn)來,以便于后續(xù)擴(kuò)展使用。
1.3 getSession 方法
@Override
public HttpSessionWrapper getSession(boolean create) {
// 從當(dāng)前請(qǐng)求線程中獲取 session
HttpSessionWrapper currentSession = getCurrentSession();
// 如果有直接返回
if (currentSession != null) {
return currentSession;
}
// 從請(qǐng)求中獲取 session,這里面會(huì)涉及到從緩存中拿session的過程
S requestedSession = getRequestedSession();
if (requestedSession != null) {
// 無效的會(huì)話id(不支持的會(huì)話存儲(chǔ)庫)請(qǐng)求屬性名稱。
// 這里看下當(dāng)前的sessionId是否有效
if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {
// 設(shè)置當(dāng)前session的最后訪問時(shí)間,用于延遲session的有效期
requestedSession.setLastAccessedTime(Instant.now());
// 將requestedSessionIdValid置為true
this.requestedSessionIdValid = true;
// 包裝session
currentSession = new HttpSessionWrapper(requestedSession, getServletContext());
// 不是新的session,如果是新的session則需要改變sessionId
currentSession.setNew(false);
// 將session設(shè)置到當(dāng)前請(qǐng)求上下文
setCurrentSession(currentSession);
// 返回session
return currentSession;
}
}
else {
// 這里處理的是無效的sessionId的情況,但是當(dāng)前請(qǐng)求線程 session有效
if (SESSION_LOGGER.isDebugEnabled()) {
SESSION_LOGGER.debug(
"No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
}
// 將invalidSessionId置為true
setAttribute(INVALID_SESSION_ID_ATTR, "true");
}
// 是否需要?jiǎng)?chuàng)建新的session
if (!create) {
return null;
}
if (SESSION_LOGGER.isDebugEnabled()) {
SESSION_LOGGER.debug(
"A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "
+ SESSION_LOGGER_NAME,
new RuntimeException(
"For debugging purposes only (not an error)"));
}
// 創(chuàng)建新的session
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
// 設(shè)置最后訪問時(shí)間,也就是指定了當(dāng)前session的有效期限
session.setLastAccessedTime(Instant.now());
// 包裝下當(dāng)前session
currentSession = new HttpSessionWrapper(session, getServletContext());
//設(shè)置到當(dāng)前請(qǐng)求線程
setCurrentSession(currentSession);
return currentSession;
}上面這段代碼有幾個(gè)點(diǎn),這里單獨(dú)來解釋下。
getCurrentSession
這是為了在同一個(gè)請(qǐng)求過程中不需要重復(fù)的去從存儲(chǔ)中獲取session,在一個(gè)新的進(jìn)來時(shí),將當(dāng)前的 session 設(shè)置到當(dāng)前請(qǐng)求中,在后續(xù)處理過程如果需要getSession就不需要再去存儲(chǔ)介質(zhì)中再拿一次。
getRequestedSession
這個(gè)是根據(jù)請(qǐng)求信息去取 session ,這里面就包括了 sessionId 解析,從存儲(chǔ)獲取 session 對(duì)象等過程。
是否創(chuàng)建新的 session 對(duì)象
在當(dāng)前請(qǐng)求中和存儲(chǔ)中都沒有獲取到 session 信息的情況下,這里會(huì)根據(jù) create 參數(shù)來判斷是否創(chuàng)建新的 session 。這里一般用戶首次登錄時(shí)或者 session 失效時(shí)會(huì)走到。
1.4 getRequestedSession
根據(jù)請(qǐng)求信息來獲取 session 對(duì)象
private S getRequestedSession() {
// 緩存的請(qǐng)求session是否存在
if (!this.requestedSessionCached) {
// 獲取 sessionId
List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver
.resolveSessionIds(this);
// 通過sessionId來從存儲(chǔ)中獲取session
for (String sessionId : sessionIds) {
if (this.requestedSessionId == null) {
this.requestedSessionId = sessionId;
}
S session = SessionRepositoryFilter.this.sessionRepository
.findById(sessionId);
if (session != null) {
this.requestedSession = session;
this.requestedSessionId = sessionId;
break;
}
}
this.requestedSessionCached = true;
}
return this.requestedSession;
}這段代碼還是很有意思的,這里獲取 sessionId 返回的是個(gè)列表。當(dāng)然這里是 SpringSession 的實(shí)現(xiàn)策略,因?yàn)橹С?session ,所以這里以列表的形式返回的。OK,繼續(xù)來看如何解析 sessionId 的:

這里可以看到 SpringSession 對(duì)于 sessionId 獲取的兩種策略,一種是基于 cookie ,一種是基于 header ;分別來看下具體實(shí)現(xiàn)。
1.4.1 CookieHttpSessionIdResolver 獲取 sessionId
CookieHttpSessionIdResolver 中獲取 sessionId 的核心代碼如下:
其實(shí)這里沒啥好說的,就是讀 cookie 。從 request 將 cookie 信息拿出來,然后遍歷找當(dāng)前 sessionId 對(duì)應(yīng)的 cookie ,這里的判斷也很簡單, 如果是以 SESSION 開頭,則表示是 SessionId ,畢竟 cookie 是共享的,不只有 sessionId,還有可能存儲(chǔ)其他內(nèi)容。
另外這里面有個(gè) jvmRoute,這個(gè)東西實(shí)際上很少能夠用到,因?yàn)榇蠖鄶?shù)情況下這個(gè)值都是null。這個(gè)我們?cè)诜治?CookieSerializer 時(shí)再來解釋。
1.4.2 HeaderHttpSessionIdResolver 獲取 sessionId
這個(gè)獲取更直接粗暴,就是根據(jù) headerName 從 header中取值。
回到 getRequestedSession ,剩下的代碼中核心的都是和 sessionRepository 這個(gè)有關(guān)系,這部分就會(huì)涉及到存儲(chǔ)部分。不在本篇的分析范圍之內(nèi),會(huì)在存儲(chǔ)實(shí)現(xiàn)部分來分析。
1.5 HttpSessionWrapper

上面的代碼中當(dāng)我們拿到 session 實(shí)例是通常會(huì)包裝下,那么用到的就是這個(gè) HttpSessionWrapper 。
HttpSessionWrapper 繼承了 HttpSessionAdapter ,這個(gè) HttpSessionAdapter 就是將SpringSession 轉(zhuǎn)換成一個(gè)標(biāo)準(zhǔn) HttpSession 的適配類。 HttpSessionAdapter 實(shí)現(xiàn)了標(biāo)準(zhǔn) servlet 規(guī)范的 HttpSession 接口。
1.5.1 HttpSessionWrapper
HttpSessionWrapper 重寫了 invalidate 方法。從代碼來看,調(diào)用該方法產(chǎn)生的影響是:
requestedSessionInvalidated 置為 true ,標(biāo)識(shí)當(dāng)前 session 失效。
將當(dāng)前請(qǐng)求中的 session 設(shè)置為 null ,那么在請(qǐng)求的后續(xù)調(diào)用中通過 getCurrentSession 將拿不到 session 信息。
當(dāng)前緩存的 session 清楚,包括sessionId,session實(shí)例等。
刪除存儲(chǔ)介質(zhì)中的session對(duì)象。
1.5.2 HttpSessionAdapter
SpringSession 和標(biāo)準(zhǔn) HttpSession 的配置器類。這個(gè)怎么理解呢,來看下一段代碼:
@Override
public Object getAttribute(String name) {
checkState();
return this.session.getAttribute(name);
}對(duì)于基于容器本身實(shí)現(xiàn)的 HttpSession 來說, getAttribute 的實(shí)現(xiàn)也是有容器本身決定。但是這里做了轉(zhuǎn)換之后, getAttribute 將會(huì)通過 SpringSession 中實(shí)現(xiàn)的方案來獲取。其他的 API 適配也是基于此實(shí)現(xiàn)。
SessionCommittingRequestDispatcher
實(shí)現(xiàn)了 RequestDispatcher 接口。關(guān)于 RequestDispatcher 可以參考這篇文章【Servlet】關(guān)于RequestDispatcher的原理 。 SessionCommittingRequestDispatcher 對(duì) forward 的行為并沒有改變。 對(duì)于 include 則是在 include 之前提交 session 。為什么這么做呢?
因?yàn)?include 方法使原先的 Servlet 和轉(zhuǎn)發(fā)到的 Servlet 都可以輸出響應(yīng)信息,即原先的 Servlet 還可以繼續(xù)輸出響應(yīng)信息;即請(qǐng)求轉(zhuǎn)發(fā)后,原先的 Servlet 還可以繼續(xù)輸出響應(yīng)信息,轉(zhuǎn)發(fā)到的 Servlet 對(duì)請(qǐng)求做出的響應(yīng)將并入原先 Servlet 的響應(yīng)對(duì)象中。
所以這個(gè)在 include 調(diào)用之前調(diào)用 commit ,這樣可以確保被包含的 Servlet 程序不能改變響應(yīng)消息的狀態(tài)碼和響應(yīng)頭。
2 響應(yīng)重寫
響應(yīng)重寫的目的是確保在請(qǐng)求提交時(shí)能夠把session保存起來。來看下 SessionRepositoryResponseWrapper 類的實(shí)現(xiàn):
這里面實(shí)現(xiàn)還就是重寫 onResponseCommitted ,也就是上面說的,在請(qǐng)求提交時(shí)能夠通過這個(gè)回調(diào)函數(shù)將 session
保存到存儲(chǔ)容器中。
2.1 session 提交
最后來看下 commitSession

這個(gè)過程不會(huì)再去存儲(chǔ)容器中拿 session 信息,而是直接從當(dāng)前請(qǐng)求中拿。如果拿不到,則在回寫 cookie 時(shí)會(huì)將當(dāng)前 session 對(duì)應(yīng)的 cookie 值設(shè)置為空,這樣下次請(qǐng)求過來時(shí)攜帶的 sessionCookie 就是空,這樣就會(huì)重新觸發(fā)登陸。
如果拿到,則清空當(dāng)前請(qǐng)求中的 session 信息,然后將 session 保存到存儲(chǔ)容器中,并且將 sessionId 回寫到 cookie 中。
上述內(nèi)容就是使用SpringSession怎么實(shí)現(xiàn)請(qǐng)求與響應(yīng)重寫,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
文章題目:使用SpringSession怎么實(shí)現(xiàn)請(qǐng)求與響應(yīng)重寫
文章位置:http://chinadenli.net/article40/jgpdho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站改版、企業(yè)網(wǎng)站制作、靜態(tài)網(wǎng)站、網(wǎng)站設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)