一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - SpringSession 請求與響應重寫的實現

SpringSession 請求與響應重寫的實現

2021-06-17 11:07glmapper Java教程

這篇文章主要介紹了SpringSession 請求與響應重寫的實現,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

我們知道, httpservletrequsethttpservletresponseservlet 標準所指定的 java 語言與 web 容器進行交互的接口。接口本身只規定 java 語言對 web 容器進行訪問的行為方式,而具體的實現是由不同的 web 容器在其內部實現的。

那么在運行期,當我們需要對 httpservletrequsethttpservletresponse 的默認實例進行擴展時,我們就可以繼承 httpservletrequestwrapperhttpservletresponsewrapper 來實現。

springsession 中因為我們要實現不依賴容器本身的 getsession 實現,因此需要擴展 httpservletrequset ,通過重寫 getsession 來實現分布式 session 的能力。下面就來看下 springsession 中對于 httpservletrequset 的擴展。

1、請求重寫

springsession 中對于請求重寫,在能力上主要體現在存儲方面,也就是 getsession 方法上。在 sessionrepositoryfilter 這個類中,是通過內部類的方式實現了對 httpservletrequsethttpservletresponse 的擴展。

1.1 httpservletrequset 擴展實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private final class sessionrepositoryrequestwrapper
            extends httpservletrequestwrapper {
    // httpservletresponse 實例
    private final httpservletresponse response;
    // servletcontext 實例
    private final servletcontext servletcontext;
 // requestedsession session對象
 private s requestedsession;
 // 是否緩存 session
 private boolean requestedsessioncached;
    // sessionid
    private string requestedsessionid;
    // sessionid 是否有效
    private boolean requestedsessionidvalid;
    // sessionid 是否失效
    private boolean requestedsessioninvalidated;
    
    // 省略方法
}

1.2 構造方法

?
1
2
3
4
5
6
private sessionrepositoryrequestwrapper(httpservletrequest request,
        httpservletresponse response, servletcontext servletcontext) {
    super(request);
    this.response = response;
    this.servletcontext = servletcontext;
}

構造方法里面將 httpservletrequest 、 httpservletresponse 以及 servletcontext 實例傳遞進來,以便于后續擴展使用。

1.3 getsession 方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@override
public httpsessionwrapper getsession(boolean create) {
 // 從當前請求線程中獲取 session
    httpsessionwrapper currentsession = getcurrentsession();
    // 如果有直接返回
    if (currentsession != null) {
        return currentsession;
    }
    // 從請求中獲取 session,這里面會涉及到從緩存中拿session的過程
    s requestedsession = getrequestedsession();
    if (requestedsession != null) {
     // 無效的會話id(不支持的會話存儲庫)請求屬性名稱。
     // 這里看下當前的sessionid是否有效
        if (getattribute(invalid_session_id_attr) == null) {
         // 設置當前session的最后訪問時間,用于延遲session的有效期
            requestedsession.setlastaccessedtime(instant.now());
            // 將requestedsessionidvalid置為true
            this.requestedsessionidvalid = true;
            // 包裝session
            currentsession = new httpsessionwrapper(requestedsession, getservletcontext());
            // 不是新的session,如果是新的session則需要改變sessionid
            currentsession.setnew(false);
            // 將session設置到當前請求上下文
            setcurrentsession(currentsession);
            // 返回session
            return currentsession;
        }
    }
    else {
        // 這里處理的是無效的sessionid的情況,但是當前請求線程 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");
    }
    // 是否需要創建新的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)"));
    }
    // 創建新的session
    s session = sessionrepositoryfilter.this.sessionrepository.createsession();
    // 設置最后訪問時間,也就是指定了當前session的有效期限
    session.setlastaccessedtime(instant.now());
    // 包裝下當前session
    currentsession = new httpsessionwrapper(session, getservletcontext());
    //設置到當前請求線程
    setcurrentsession(currentsession);
    return currentsession;
}

上面這段代碼有幾個點,這里單獨來解釋下。

getcurrentsession

這是為了在同一個請求過程中不需要重復的去從存儲中獲取session,在一個新的進來時,將當前的 session 設置到當前請求中,在后續處理過程如果需要getsession就不需要再去存儲介質中再拿一次。

getrequestedsession

這個是根據請求信息去取 session ,這里面就包括了 sessionid 解析,從存儲獲取 session 對象等過程。

是否創建新的 session 對象

在當前請求中和存儲中都沒有獲取到 session 信息的情況下,這里會根據 create 參數來判斷是否創建新的 session 。這里一般用戶首次登錄時或者 session 失效時會走到。

1.4 getrequestedsession

根據請求信息來獲取 session 對象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private s getrequestedsession() {
 // 緩存的請求session是否存在
    if (!this.requestedsessioncached) {
  // 獲取 sessionid
  list<string> sessionids = sessionrepositoryfilter.this.httpsessionidresolver
        .resolvesessionids(this);
  // 通過sessionid來從存儲中獲取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 返回的是個列表。當然這里是 springsession 的實現策略,因為支持 session ,所以這里以列表的形式返回的。ok,繼續來看如何解析 sessionid 的:

 

 
SpringSession 請求與響應重寫的實現

 

這里可以看到 springsession 對于 sessionid 獲取的兩種策略,一種是基于 cookie ,一種是基于 header ;分別來看下具體實現。

1.4.1 cookiehttpsessionidresolver 獲取 sessionid

cookiehttpsessionidresolver 中獲取 sessionid 的核心代碼如下:

 

 
SpringSession 請求與響應重寫的實現 

 

其實這里沒啥好說的,就是讀 cookie 。從 requestcookie 信息拿出來,然后遍歷找當前 sessionid 對應的 cookie ,這里的判斷也很簡單, 如果是以 session 開頭,則表示是 sessionid ,畢竟 cookie 是共享的,不只有 sessionid,還有可能存儲其他內容。

另外這里面有個 jvmroute,這個東西實際上很少能夠用到,因為大多數情況下這個值都是null。這個我們在分析 cookieserializer 時再來解釋。

1.4.2 headerhttpsessionidresolver 獲取 sessionid

 

 

SpringSession 請求與響應重寫的實現

這個獲取更直接粗暴,就是根據 headernameheader中取值。

回到 getrequestedsession ,剩下的代碼中核心的都是和 sessionrepository 這個有關系,這部分就會涉及到存儲部分。不在本篇的分析范圍之內,會在存儲實現部分來分析。

1.5 httpsessionwrapper

 

 

SpringSession 請求與響應重寫的實現

上面的代碼中當我們拿到 session 實例是通常會包裝下,那么用到的就是這個 httpsessionwrapper 。

httpsessionwrapper 繼承了 httpsessionadapter ,這個 httpsessionadapter 就是將springsession 轉換成一個標準 httpsession 的適配類。 httpsessionadapter 實現了標準 servlet 規范的 httpsession 接口。

1.5.1 httpsessionwrapper

httpsessionwrapper 重寫了 invalidate 方法。從代碼來看,調用該方法產生的影響是:

  • requestedsessioninvalidated 置為 true ,標識當前 session 失效。
  • 將當前請求中的 session 設置為 null ,那么在請求的后續調用中通過 getcurrentsession 將拿不到 session 信息。
  • 當前緩存的 session 清楚,包括sessionid,session實例等。
  • 刪除存儲介質中的session對象。

 1.5.2 httpsessionadapter

springsession 和標準 httpsession 的配置器類。這個怎么理解呢,來看下一段代碼:

?
1
2
3
4
5
@override
public object getattribute(string name) {
    checkstate();
    return this.session.getattribute(name);
}

對于基于容器本身實現的 httpsession 來說, getattribute 的實現也是有容器本身決定。但是這里做了轉換之后, getattribute 將會通過 springsession 中實現的方案來獲取。其他的 api 適配也是基于此實現。

sessioncommittingrequestdispatcher

實現了 requestdispatcher 接口。關于 requestdispatcher 可以參考這篇文章【servlet】關于requestdispatcher的原理 。 sessioncommittingrequestdispatcherforward 的行為并沒有改變。 對于 include 則是在 include 之前提交 session 。為什么這么做呢?

因為 include 方法使原先的 servlet 和轉發到的 servlet 都可以輸出響應信息,即原先的 servlet 還可以繼續輸出響應信息;即請求轉發后,原先的 servlet 還可以繼續輸出響應信息,轉發到的 servlet 對請求做出的響應將并入原先 servlet 的響應對象中。

所以這個在 include 調用之前調用 commit ,這樣可以確保被包含的 servlet 程序不能改變響應消息的狀態碼和響應頭。

2 響應重寫

響應重寫的目的是確保在請求提交時能夠把session保存起來。來看下 sessionrepositoryresponsewrapper 類的實現:

 

 
SpringSession 請求與響應重寫的實現 

 

這里面實現還就是重寫 onresponsecommitted ,也就是上面說的,在請求提交時能夠通過這個回調函數將 session

保存到存儲容器中。

2.1 session 提交

最后來看下 commitsession

 

 
SpringSession 請求與響應重寫的實現

 

這個過程不會再去存儲容器中拿 session 信息,而是直接從當前請求中拿。如果拿不到,則在回寫 cookie 時會將當前 session 對應的 cookie 值設置為空,這樣下次請求過來時攜帶的 sessioncookie 就是空,這樣就會重新觸發登陸。

如果拿到,則清空當前請求中的 session 信息,然后將 session 保存到存儲容器中,并且將 sessionid 回寫到 cookie 中。

小結

本篇主要對 springsession 中重寫 requestresponse 進行了分析。通過重寫 request 請求來將 session 的存儲與存儲容器關聯起來,通過重寫 response 來處理 session 提交,將 session 保存到存儲容器中。

后面我們會繼續來分析 springsession 的源碼。最近也在學習鏈路跟蹤相關的技術,也準備寫一寫,有興趣的同學可以一起討論。 希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://juejin.im/post/5bf8f179e51d450c487d0df2

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费av在线看 | 日本公与妇中文在线 | 男人天堂中文字幕 | 四虎影视4hu最新地址在线884 | 门房秦大爷小说 | 九九99香蕉在线视频美国毛片 | 亚洲美女人黄网成人女 | 波多野结衣不卡 | 偷偷狠狠的日日高清完整视频 | 香蕉精品视频 | 日韩激情视频在线观看 | 国产情侣偷国语对白 | 国产成人亚洲综合a∨婷婷 国产成人亚洲精品乱码在线观看 | 天生奶水1v1高h | 国产精品夜色视频一级区 | 免费看国产精品麻豆 | 国产卡一卡二卡三卡四 | 欧美视频一级 | 精品在线观看一区 | 美女曰逼视频 | 日本人妖在线 | 無码一区中文字幕少妇熟女网站 | 国产日韩欧美综合一区二区三区 | 日韩在线免费播放 | 好爽轻点太大了太深了 | 出轨娇妻的呻吟1—9 | 国产精品亚洲午夜一区二区三区 | 天天天做天天天天爱天天想 | 国产卡一卡二卡四卡无卡 | 久久日本片精品AAAAA国产 | 天堂激情网 | 国产精品片 | 婷婷色网| 俄罗斯三级在线观看级 | 日韩精品欧美 | 免费特黄一级欧美大片在线看 | 99爱在线观看精品视频 | 国产欧美日韩精品一区二 | 国内精品久久久久久久久 | 色婷婷久久综合中文久久一本` | 好男人影视社区www在线观看 |