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

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

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

服務(wù)器之家 - 編程語言 - JAVA教程 - spring-session簡介及實現(xiàn)原理源碼分析

spring-session簡介及實現(xiàn)原理源碼分析

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

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

一:spring-session介紹

1.簡介

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

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

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

?
1
spring-session是spring旗下的一個項目,把servlet容器實現(xiàn)的httpSession替換為spring-session,專注于解決 session管理問題??珊唵慰焖偾覠o縫的集成到我們的應(yīng)用中。

2.支持功能

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

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

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

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

3.集成方式

集成方式非常簡單,直接看官網(wǎng)的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準(zhǔn)備,且把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框架內(nèi)部剖析

1.框架高層抽象結(jié)構(gòu)圖

spring-session簡介及實現(xiàn)原理源碼分析

2.spring-session重寫servlet request 及 redis實現(xiàn)存儲相關(guān)問題

spring-session無縫替換應(yīng)用服務(wù)器的request大概原理是:
1.自定義個Filter,實現(xiàn)doFilter方法
2.繼承 HttpServletRequestWrapper 、HttpServletResponseWrapper 類,重寫getSession等相關(guān)方法(在這些方法里調(diào)用相關(guān)的 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源碼,也是實現(xiàn)上面第一到第三步的關(guān)鍵類 **/
public class SessionRepositoryFilter<S extends ExpiringSession>
    extends OncePerRequestFilter {
 
  /** session存儲容器接口,redis、mongoDB、genfire等數(shù)據(jù)庫都是實現(xiàn)該接口 **/
  private final SessionRepository<S> sessionRepository;
 
  private ServletContext servletContext;
  /**
   sessionID的傳遞方式接口。目前spring-session自帶兩個實現(xiàn)類
   1.cookie方式 :CookieHttpSessionStrategy
   2.http header 方式:HeaderHttpSessionStrategy
   當(dāng)然,我們也可以自定義其他方式。
  **/
  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來實現(xiàn)的。
    每個瀏覽器擁有一個sessionID,但是這個sessionID擁有多個別名(根據(jù)瀏覽器的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;
  }
   /**
  該方法相當(dāng)于重寫了doFilter,只是spring-session又做了多一層封裝。
  在這個方法里創(chuàng)建自定義的 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位于過濾器鏈的第一個,那么后續(xù)的Filter,
       以及到達(dá)最后的控制層所獲取的 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到存儲容器,我們可能會在一個控制層里多次調(diào)用session的操作方法
      如果我們每次對session的操作都持久化到存儲容器,必定會帶來性能的影響。比如redis
      所以我們可以在整個控制層執(zhí)行完畢了,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或者h(yuǎn)eader
        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或者h(yuǎn)eader寫回給瀏覽器保存
        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存儲容器中根據(jù)sessionID獲取session
      S session = SessionRepositoryFilter.this.sessionRepository
          .getSession(sessionId);
      if (session == null) {
        return null;
      }
      // 設(shè)置sesison的最后訪問時間,以防過期
      session.setLastAccessedTime(System.currentTimeMillis());
      return session;
    }
     /**
     這個方法是不是很熟悉,下面還有個getSession()才更加熟悉。沒錯,就是在這里重新獲取session方法
     **/
    @Override
    public HttpSessionWrapper getSession(boolean create) {
      //快速獲取session,可以理解為一級緩存、二級緩存這種關(guān)系
      HttpSessionWrapper currentSession = getCurrentSession();
      if (currentSession != null) {
        return currentSession;
      }
      //從httpSessionStratge里面根據(jù)cookie或者h(yuǎn)eader獲取sessionID
      String requestedSessionId = getRequestedSessionId();
      if (requestedSessionId != null
          && getAttribute(INVALID_SESSION_ID_ATTR) == null) {                                          
        //從存儲容器獲取session以及設(shè)置當(dāng)次初始化屬性                     
        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訪問者是初次訪問服務(wù)器,則為他創(chuàng)建個新的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());
      }
    }
  }
}

總結(jié)

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 暖暖的免费观看高清视频韩国 | 亚洲成人免费看 | 精品国产91久久久久 | 免费二区 | 免费日本在线 | 三极片在线观看 | 8mav福利视频 | 亚洲国产精品久久久久 | 色婷婷精品 | 亚洲成在人网站天堂一区二区 | 操娇妻 | 九九爱这里只有精品 | 穆挂英风流艳史小说 | 成人伊人青草久久综合网破解版 | 91拍拍| 动漫美女人物被黄漫小说 | 99任你躁精品视频 | 好大好硬好深好爽想要吃奶 | 美国高清xxxxx18 | 福利片免费一区二区三区 | 草久热| 国产天天在线 | 欧美乱妇高清无乱码视频在线 | 亚洲图片综合区 | 免费高清视频免费观看 | 青柠影视在线播放观看高清 | 小嫩videos | 经典千人斩一区二区视频 | 俄罗斯一级毛片免费播放 | 调教小龙女 | 国产一久久香蕉国产线看观看 | 海绵宝宝第二季全集免费观看 | 亚洲色图亚洲色图 | 亚洲黄视频在线观看 | 久久99精国产一区二区三区四区 | 久久国产视频网站 | 午夜精品网 | 日本免费三片在线播放 | 国产普通话对白露脸流出 | 国产99在线a视频 | 欧美一卡2卡三卡4卡5卡免费观看 |