SpringSession系列-集成SpringBoot

springSessionspring 旗下的一個項目,把 servlet 容器實現的 httpSession替換爲springSession,專一於解決session管理問題。可簡單快速且無縫的集成到咱們的應用中。本文經過一個案例,使用SpringBoot來集成 SpringSession,而且使用Redis做爲存儲來實踐下SpringSession 的使用。java

環境準備

由於須要使用Redis做爲底層Session的存儲介質,實現分佈式session,所以須要安裝Redisweb

Redis 安裝

一、從官網下載最新版的Redisredis

二、解壓spring

tar zxvf redis-5.0.0.tar.gz
複製代碼

三、編譯測試瀏覽器

sudo make test
複製代碼

四、編譯安裝緩存

sudo make install
複製代碼

五、安裝問題bash

若是您以前安裝過,重複安裝且沒有卸載乾淨的話,會報下面的錯服務器

make[1]: *** [test] Error 1 
make: *** [test] Error 2
複製代碼

解決這個錯誤,執行下面的語句便可:cookie

make distclean 
make 
make test
複製代碼

正確安裝姿式以下:session

六、啓動Redis 在您的Redis安裝目錄下,有 redis-server ,執行該腳本命令:

OK,到這裏,Redis的安裝工做完畢。

SpringBoot 工程準備

這裏咱們直接經過Idea來構建咱們的SpringBoot工程。

File->New->Project : Spring Initializr
複製代碼

OK,SpringBoot 工程準備完畢,這裏選擇建立的是一個Web工程。

集成

集成主要是依賴引入,這裏須要redissession的依賴

依賴引入

<dependencies>
    <!--redis 依賴-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--sessions 依賴-->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
</dependencies>
複製代碼

配置application.properties

#服務端口
server.port=8080
#redi主機地址
spring.redis.host=localhost
#redis服務端口
spring.redis.port=6379

# spring session使用存儲類型,spirngboot默認就是使用redis方式,若是不想用能夠填none。
spring.session.store-type=redis
複製代碼

在啓動類中加入@EnableRedisHttpSession 註解

@SpringBootApplication
@EnableRedisHttpSession
public class SpringBootSessionApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootSessionApplication.class, args);
    }
}
複製代碼

測試

先來編寫一個Controller

/** * SessionController * * @author: glmapper@leishu * @since: 18/11/3 下午3:16 * @version 1.0 **/
@Controller
@RequestMapping(value = "/")
public class SessionController {
    
    @ResponseBody
    @RequestMapping(value = "/session")
    public Map<String, Object> getSession(HttpServletRequest request) {
        request.getSession().setAttribute("userName", "glmapper");
        Map<String, Object> map = new HashMap<>();
        map.put("sessionId", request.getSession().getId());
        return map;
    }
    
    @ResponseBody
    @RequestMapping(value = "/get")
    public String get(HttpServletRequest request) {
        String userName = (String) request.getSession().getAttribute("userName");
        return userName;
    }
}

複製代碼

測試結果

啓動SpringBoot 工程;而後瀏覽器中輸入地址 http://localhost:8080/session;

這裏對應執行的是咱們上面 Controller中的第一個方法 getSession,這個方法向 session中設置了一個值。

下面咱們執行:http://localhost:8080/get 這裏是從session中取值:

到此,SpringBoot 整合 SpringSession 的過程就完成了。這裏咱們只是引入了依賴,而後作了簡單的配置,那麼咱們的請求是如何被 SpringSession 處理的呢?從咱們一向的認知來看,對於基於Servlet規範的容器(SpringBoot 使用的是嵌入式Tomcat)的應用,請求最早被處理的是Filter。咱們在基於Spring+SpringMvc這套技術棧開發時,若是咱們須要作權限管理,經過會基於Filter或者攔截器。可是這裏貌似咱們什麼也沒作,可是請求確實被SpringSession處理了。OK,咱們來扒一扒。

SpringSession 是如何處理請求的?

上面這張截圖想必你們都不陌生,是 SpringBoot的啓動日誌;上圖紅色框內的是當前應用註冊是 Filter信息,從這裏能夠看到有個和 session 有關的 Filter:sessionRepositoryFilter;這個 bean對應的類是:

org.springframework.boot.autoconfigure.session.SessionRepositoryFilterConfiguration.ConditionalOnBean=
org.springframework.session.web.http.SessionRepositoryFilter
複製代碼

在這裏找到了

這裏涉及到SpringBoot的自動配置,從spring-boot-autoconfig包下加載spring-autoconfigure-metadata.properties 配置文件,而後獲取全部支持自動配置的信息;SpringSession 也在其中。關於如何加載而且註冊不在本文的範疇以內,咱們繼續來分析SpringSession的處理過程。

SpringSession 的處理過程

從上面SpringBoot的啓動過程咱們找到了處理sessionFilter,而後知道了它是經過自動配置的方式被註冊到當前的容器而且來處理請求。

@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
複製代碼

SessionRepositoryFilter的定義來看:

  • 一、使用了Order,而且配置了一個很小的值(Integer.MIN_VALUE + 50),以此來確保sessionFilterFilter鏈中被優先執行。
  • 二、集成了OncePerRequestFilter,確保在一次請求只經過一次filter,而不須要重複執行

爲何 sessionFilter 要被優先執行呢?由於咱們的請求被包裝了,若是SessionRepositoryFilter不優先處理請求,可能會致使後續的請求行爲不一致,這裏涉及到 springSession無縫替換應用服務器的request的原理:

  • 1.自定義個Filter,實現doFilter方法
  • 2.繼承 HttpServletRequestWrapperHttpServletResponseWrapper 類,重寫getSession等相關方法(在這些方法裏調用相關的 session存儲容器操做類)。
  • 3.自定義requestresponse類;並把它們分別傳遞到過濾器鏈
  • 4.把該filter配置到過濾器鏈的第一個位置上

OK,瞭解了這些背景,咱們來跟蹤下整個處理流程。

一、斷點到 doFilterInternal

從這裏能夠看到requestresponse類被包裝了。

二、斷點到 getSession

這裏是從Redis中拿咱們session數據的地方

  • 先從咱們當前servlet容器中去拿,若是拿到則直接返回

  • Redis中取

    這裏會有一個緩存處理,並不是是每次都到Reids中去查一次,避免一次與Reids的交互。

    • 若是緩存當前應用容器緩存中有,則直接返回當前被緩存的session
    • 若是沒有,則從請求中獲取sessionId,而且根據當前sessionIdReids中查找session數據
    • 更新緩存session,sessionId,requestedSessionCached等數據狀態
  • 若是Redis中有,則更新session相關信息並返回

  • 若是Reids中沒有找到,則根據 create 來判斷是否建立新的session

斷點到 readCookieValues

SpringSession提供了兩種保存和傳遞SessionId的方式,一種是基於Cookie的,一種是基於Header的。SpringSession中默認使用的是基於Cookie的方式。readCookieValues 就是實現如何從Cookie中獲取sessionId的。

這個過程其實很簡單,先是從request中獲取當前請求攜帶的因此的Cookie信息,而後將匹配到的 cookieName「SESSION」Cookie進行解析。

斷點到 RedisOperationsSessionRepository -> getSession

這裏是從Redis中取session數據的地方

  • 根據sessionIdRedis中取到 entries 數據
  • 構建 RedisSession 並返回

斷點到 commitSession

commitSession做用是經過HttpSessionIdResolversessionId寫到response,而且進行持久化。

這裏的 session 實際上是已經更新過狀態的,好比從新設置了 session 的過時時間等。session 提交實際上就意味着當前請求已經處理完畢了。

小結

本文先介紹瞭如何使用 SpringBoot 集成 SpringSession,而且以 Redis 做爲存儲。而後簡單分析了 SpringSession 的處理過程,本文對 SpringSession 的原理部分沒有進行深刻分析,下一篇分析下SpringSession的原理。

相關文章
相關標籤/搜索