激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

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

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

服務器之家 - 編程語言 - JAVA教程 - spring-session簡介及實現原理源碼分析

spring-session簡介及實現原理源碼分析

2021-02-24 13:57孤客_ JAVA教程

這篇文章主要介紹了spring-session簡介及實現原理源碼分析,具有一定參考價值,需要的朋友可以了解下。

一:spring-session介紹

1.簡介

session一直都是我們做集群時需要解決的一個難題,過去我們可以從serlvet容器上解決,比如開源servlet容器-tomcat提供的tomcat-redis-session-manager、memcached-session-manager。

或者通過nginx之類的負載均衡做ip_hash,路由到特定的服務器上..

但是這兩種辦法都存在弊端。

?
1
spring-session是spring旗下的一個項目,把servlet容器實現的httpSession替換為spring-session,專注于解決 session管理問題。可簡單快速且無縫的集成到我們的應用中。

2.支持功能

1)輕易把session存儲到第三方存儲容器,框架提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多種存儲session的容器的方式。

2)同一個瀏覽器同一個網站,支持多個session問題。

3)RestfulAPI,不依賴于cookie。可通過header來傳遞jessionID

4)WebSocket和spring-session結合,同步生命周期管理。

3.集成方式

集成方式非常簡單,直接看官網的samplesandguide。http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/

主要分為以下幾個集成步驟:

1)引入依賴jar包

2)注解方式或者xml方式配置特定存儲容器的存儲方式,如redis的xml配置方式

?
1
2
3
4
5
<context:annotation-config/> 
/** 初始化一切spring-session準備,且把springSessionFilter放入IOC     **/
<beanclass="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/> 
/** 這是存儲容器的鏈接池 **/
<beanclass="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>

3)xml方式配置 web.xml ,配置 springSessionFilter到 filter chain中

?
1
2
3
4
5
6
7
8
9
<filter>
     <filter-name>springSessionRepositoryFilter</filter-name>
     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
   </filter>
   <filter-mapping>
     <filter-name>springSessionRepositoryFilter</filter-name>
     <url-pattern>/*</url-pattern>
     <dispatcher>REQUEST</dispatcher><dispatcher>ERROR</dispatcher>
   </filter-mapping>

二:spring-session框架內部剖析

1.框架高層抽象結構圖

spring-session簡介及實現原理源碼分析

2.spring-session重寫servlet request 及 redis實現存儲相關問題

spring-session無縫替換應用服務器的request大概原理是:
1.自定義個Filter,實現doFilter方法
2.繼承 HttpServletRequestWrapper 、HttpServletResponseWrapper 類,重寫getSession等相關方法(在這些方法里調用相關的 session存儲容器操作類)。
3.在 第一步的doFilter中,new 第二步 自定義的request和response的類。并把它們分別傳遞 到 過濾器鏈
4.把該filter配置到 過濾器鏈的第一個位置上

?
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/** 這個類是spring-session的1.30源碼,也是實現上面第一到第三步的關鍵類 **/
public class SessionRepositoryFilter<S extends ExpiringSession>
    extends OncePerRequestFilter {
 
  /** session存儲容器接口,redis、mongoDB、genfire等數據庫都是實現該接口 **/
  private final SessionRepository<S> sessionRepository;
 
  private ServletContext servletContext;
  /**
   sessionID的傳遞方式接口。目前spring-session自帶兩個實現類
   1.cookie方式 :CookieHttpSessionStrategy
   2.http header 方式:HeaderHttpSessionStrategy
   當然,我們也可以自定義其他方式。
  **/
  private MultiHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
 
  public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
    if (sessionRepository == null) {
      throw new IllegalArgumentException("sessionRepository cannot be null");
    }
    this.sessionRepository = sessionRepository;
  }
 
 
  public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
    if (httpSessionStrategy == null) {
      throw new IllegalArgumentException("httpSessionStrategy cannot be null");
    }
    /**
    通過前面的spring-session功能介紹,我們知道spring-session可以支持單瀏覽器多
    session, 就是通過MultiHttpSessionStrategyAdapter來實現的。
    每個瀏覽器擁有一個sessionID,但是這個sessionID擁有多個別名(根據瀏覽器的tab)。如:
        別名1 sessionID
        別名2 sessionID
        ...
        而這個別名通過url來傳遞,這就是單瀏覽器多session原理了
        **/
    this.httpSessionStrategy = new MultiHttpSessionStrategyAdapter(
        httpSessionStrategy);
  }
 
 
  public void setHttpSessionStrategy(MultiHttpSessionStrategy httpSessionStrategy) {
    if (httpSessionStrategy == null) {
      throw new IllegalArgumentException("httpSessionStrategy cannot be null");
    }
    this.httpSessionStrategy = httpSessionStrategy;
  }
   /**
  該方法相當于重寫了doFilter,只是spring-session又做了多一層封裝。
  在這個方法里創建自定義的 request和response,然后傳遞到過濾器鏈filterChain
   **/
  @Override
  protected void doFilterInternal(HttpServletRequest request,
      HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
    request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
        /**
        spring-session重寫的ServletRequest。這個類繼承了HttpServletRequestWrapper
        **/
    SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
        request, response, this.servletContext);
    SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
        wrappedRequest, response);
 
    HttpServletRequest strategyRequest = this.httpSessionStrategy
        .wrapRequest(wrappedRequest, wrappedResponse);
    HttpServletResponse strategyResponse = this.httpSessionStrategy
        .wrapResponse(wrappedRequest, wrappedResponse);
 
    try {
       /**
       傳遞自定義 request和response到鏈中,想象下如果
       該spring-sessionFilter位于過濾器鏈的第一個,那么后續的Filter,
       以及到達最后的控制層所獲取的 request和response,是不是就是我們自定義的了?
       **/
      filterChain.doFilter(strategyRequest, strategyResponse);
    }
    finally {
      wrappedRequest.commitSession();
    }
  }
 
  public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
  }
 
  /**
  這個就是Servlet response的重寫類了
   */
  private final class SessionRepositoryResponseWrapper
      extends OnCommittedResponseWrapper {
 
    private final SessionRepositoryRequestWrapper request;
 
 
    SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request,
        HttpServletResponse response) {
      super(response);
      if (request == null) {
        throw new IllegalArgumentException("request cannot be null");
      }
      this.request = request;
    }
     /**
      這步是持久化session到存儲容器,我們可能會在一個控制層里多次調用session的操作方法
      如果我們每次對session的操作都持久化到存儲容器,必定會帶來性能的影響。比如redis
      所以我們可以在整個控制層執行完畢了,response返回信息到瀏覽器時,才持久化session
     **/
    @Override
    protected void onResponseCommitted() {
      this.request.commitSession();
    }
  }
 
  /**
  spring-session 的request重寫類,這幾乎是最重要的一個重寫類。里面重寫了獲取getSession,Session等方法以及類
   */
  private final class SessionRepositoryRequestWrapper
      extends HttpServletRequestWrapper {
    private Boolean requestedSessionIdValid;
    private boolean requestedSessionInvalidated;
    private final HttpServletResponse response;
    private final ServletContext servletContext;
 
    private SessionRepositoryRequestWrapper(HttpServletRequest request,
        HttpServletResponse response, ServletContext servletContext) {
      super(request);
      this.response = response;
      this.servletContext = servletContext;
    }
 
    /**
     * Uses the HttpSessionStrategy to write the session id to the response and
     * persist the Session.
     */
    private void commitSession() {
      HttpSessionWrapper wrappedSession = getCurrentSession();
      if (wrappedSession == null) {
          // session失效,刪除cookie或者header
        if (isInvalidateClientSession()) {
          SessionRepositoryFilter.this.httpSessionStrategy
              .onInvalidateSession(this, this.response);
        }
      }
      else {
        S session = wrappedSession.getSession();
        SessionRepositoryFilter.this.sessionRepository.save(session);
        if (!isRequestedSessionIdValid()
            || !session.getId().equals(getRequestedSessionId())) {
        // 把cookie或者header寫回給瀏覽器保存
        SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,
              this, this.response);
        }
      }
    }
 
    @SuppressWarnings("unchecked")
    private HttpSessionWrapper getCurrentSession() {
      return (HttpSessionWrapper) getAttribute(CURRENT_SESSION_ATTR);
    }
 
    private void setCurrentSession(HttpSessionWrapper currentSession) {
      if (currentSession == null) {
        removeAttribute(CURRENT_SESSION_ATTR);
      }
      else {
        setAttribute(CURRENT_SESSION_ATTR, currentSession);
      }
    }
 
    @SuppressWarnings("unused")
    public String changeSessionId() {
      HttpSession session = getSession(false);
 
      if (session == null) {
        throw new IllegalStateException(
            "Cannot change session ID. There is no session associated with this request.");
      }
 
      // eagerly get session attributes in case implementation lazily loads them
      Map<String, Object> attrs = new HashMap<String, Object>();
      Enumeration<String> iAttrNames = session.getAttributeNames();
      while (iAttrNames.hasMoreElements()) {
        String attrName = iAttrNames.nextElement();
        Object value = session.getAttribute(attrName);
 
        attrs.put(attrName, value);
      }
 
      SessionRepositoryFilter.this.sessionRepository.delete(session.getId());
      HttpSessionWrapper original = getCurrentSession();
      setCurrentSession(null);
 
      HttpSessionWrapper newSession = getSession();
      original.setSession(newSession.getSession());
 
      newSession.setMaxInactiveInterval(session.getMaxInactiveInterval());
      for (Map.Entry<String, Object> attr : attrs.entrySet()) {
        String attrName = attr.getKey();
        Object attrValue = attr.getValue();
        newSession.setAttribute(attrName, attrValue);
      }
      return newSession.getId();
    }
    // 判斷session是否有效
    @Override
    public boolean isRequestedSessionIdValid() {
      if (this.requestedSessionIdValid == null) {
        String sessionId = getRequestedSessionId();
        S session = sessionId == null ? null : getSession(sessionId);
        return isRequestedSessionIdValid(session);
      }
 
      return this.requestedSessionIdValid;
    }
 
    private boolean isRequestedSessionIdValid(S session) {
      if (this.requestedSessionIdValid == null) {
        this.requestedSessionIdValid = session != null;
      }
      return this.requestedSessionIdValid;
    }
 
    private boolean isInvalidateClientSession() {
      return getCurrentSession() == null && this.requestedSessionInvalidated;
    }
 
    private S getSession(String sessionId) {
       // 從session存儲容器中根據sessionID獲取session
      S session = SessionRepositoryFilter.this.sessionRepository
          .getSession(sessionId);
      if (session == null) {
        return null;
      }
      // 設置sesison的最后訪問時間,以防過期
      session.setLastAccessedTime(System.currentTimeMillis());
      return session;
    }
     /**
     這個方法是不是很熟悉,下面還有個getSession()才更加熟悉。沒錯,就是在這里重新獲取session方法
     **/
    @Override
    public HttpSessionWrapper getSession(boolean create) {
      //快速獲取session,可以理解為一級緩存、二級緩存這種關系
      HttpSessionWrapper currentSession = getCurrentSession();
      if (currentSession != null) {
        return currentSession;
      }
      //從httpSessionStratge里面根據cookie或者header獲取sessionID
      String requestedSessionId = getRequestedSessionId();
      if (requestedSessionId != null
          && getAttribute(INVALID_SESSION_ID_ATTR) == null) {                                          
        //從存儲容器獲取session以及設置當次初始化屬性                     
        S session = getSession(requestedSessionId);
        if (session != null) {
          this.requestedSessionIdValid = true;
          currentSession = new HttpSessionWrapper(session, getServletContext());
          currentSession.setNew(false);
          setCurrentSession(currentSession);
          return currentSession;
        }
        else {
 
          if (SESSION_LOGGER.isDebugEnabled()) {
            SESSION_LOGGER.debug(
                "No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
          }
          setAttribute(INVALID_SESSION_ID_ATTR, "true");
        }
      }
      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)"));
      }
      // 如果該瀏覽器或者其他http訪問者是初次訪問服務器,則為他創建個新的session
      S session = SessionRepositoryFilter.this.sessionRepository.createSession();
      session.setLastAccessedTime(System.currentTimeMillis());
      currentSession = new HttpSessionWrapper(session, getServletContext());
      setCurrentSession(currentSession);
      return currentSession;
    }
 
    @Override
    public ServletContext getServletContext() {
      if (this.servletContext != null) {
        return this.servletContext;
      }
      // Servlet 3.0+
      return super.getServletContext();
    }
 
    @Override
    public HttpSessionWrapper getSession() {
      return getSession(true);
    }
 
    @Override
    public String getRequestedSessionId() {
      return SessionRepositoryFilter.this.httpSessionStrategy
          .getRequestedSessionId(this);
    }
 
    /**
    HttpSession的重寫類
     */
    private final class HttpSessionWrapper extends ExpiringSessionHttpSession<S> {
 
      HttpSessionWrapper(S session, ServletContext servletContext) {
        super(session, servletContext);
      }
 
      @Override
      public void invalidate() {
        super.invalidate();
        SessionRepositoryRequestWrapper.this.requestedSessionInvalidated = true;
        setCurrentSession(null);
        SessionRepositoryFilter.this.sessionRepository.delete(getId());
      }
    }
  }
}

總結

以上就是本文關于spring-session簡介及實現原理源碼分析的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出!

原文鏈接:http://blog.csdn.net/wojiaolinaaa/article/details/62424642

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 少妇淫片免费一级毛片 | 黄色免费在线电影 | 精品国产一区二区三区久久久 | 337p粉嫩大胆噜噜噜亚瑟影院 | 天天操天天骑 | 精品中文视频 | 色片免费在线观看 | 久久91久久久久麻豆精品 | 日本视频免费 | 依人网站 | 久久精品国产清自在天天线 | 中文字幕精品一区久久久久 | 色综合久久久久综合99 | 黄色网页在线观看 | 爱性久久久久久久 | 亚洲国产成人久久成人52 | 久久久在线 | 泰剧19禁啪啪无遮挡 | 日产精品一区二区三区在线观看 | 国产一区视频在线观看免费 | 色中色综合网 | 日韩av一二三区 | 国产精品欧美日韩一区二区 | 性少妇chinesevideo | 日韩视频―中文字幕 | 香蕉久草在线 | 国产高潮好爽受不了了夜色 | 万圣街在线观看免费完整版 | 欧美一级黄色免费 | 伦一区二区三区中文字幕v亚洲 | 一级片a | 免费人成在线播放 | av在线官网 | 日本在线不卡一区二区 | 日本网站一区二区三区 | 日日噜噜噜夜夜狠狠久久蜜桃 | 成人 精品 | 国产1区2区在线 | 一级黄片毛片免费看 | 国产日韩在线视频 | aa国产视频一区二区 |