Spring Session

消息模式

因?yàn)閔ttp是本身是一個(gè)無(wú)狀態(tài)協(xié)議篇梭。為了記錄用戶的狀態(tài)弓坞,session機(jī)制就應(yīng)運(yùn)而生了,servlet的標(biāo)準(zhǔn)本身是包含session的蚤氏,Tomcat會(huì)把session的信息存儲(chǔ)在服務(wù)器內(nèi)存里代赁,Request提供了獲取session的方法扰她。然而這種session數(shù)據(jù)沒(méi)有一定的持久化機(jī)制,而且難以實(shí)現(xiàn)應(yīng)用服務(wù)器的水平擴(kuò)展芭碍。在負(fù)載均衡器 + 應(yīng)用服務(wù)器集群的架構(gòu)中徒役,session共享是一個(gè)基本的要求。Spring Session支持把session信息以各種形式存儲(chǔ)窖壕,Spring Session支持把session信息以各種形式存儲(chǔ)忧勿,比如數(shù)據(jù)庫(kù)或者Redis。項(xiàng)目上一般都是將Session存放在Redis中瞻讽,session數(shù)據(jù)都是有時(shí)效性的鸳吸,Redis的數(shù)據(jù)超時(shí)機(jī)制可以很好的完成session信息的清除;此外速勇,Redis的數(shù)據(jù)訪問(wèn)速度更快晌砾,對(duì)于時(shí)效性較強(qiáng)的session數(shù)據(jù),會(huì)有比較好的加速效果烦磁。
spring redis

使用

在web.xml文件中添加一下過(guò)濾器养匈。

    <filter>
        <filter-name>spring-session</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>springSession</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>spring-session</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

注意這里的

<init-param>
    <param-name>targetBeanName</param-name>
    <param-value>springSession</param-value>
</init-param>

這里配置的這個(gè)targetBeanName在下面的配置文件中是有用到的哼勇!如果未指定init-param參數(shù)的話,DelegatingFilterProxy就會(huì)把filter-name作為要查找的Bean對(duì)象呕乎,這也是DelegatingFilterProxy類的作用猴蹂。如果配置了,就會(huì)去找 ID 為 "springSession" 的Bean 作為 Spring-Session的 過(guò)濾器楣嘁。 可以看出每一個(gè)請(qǐng)求都會(huì)經(jīng)過(guò)該filter,經(jīng)過(guò)該filter的請(qǐng)求也會(huì)相應(yīng)的經(jīng)過(guò)springSessionRepositoryFilter這個(gè)過(guò)濾器珍逸。

redis.useSentinel=false

redis.ip=localhost
redis.port=6379
redis.db=2
redis.password=
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- Root Context: defines shared resources visible to all other web components -->

    <!-- 引入config.properties屬性文件 -->
    <context:property-placeholder location="classpath:config.properties"/>

    <bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
    </bean>

    <bean id="redisSentinelConfiguration"
          class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        <property name="master">
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <property name="name" value="mymaster"/>
            </bean>
        </property>
        <property name="sentinels" ref="redisNodes"/>
    </bean>

    <bean id="redisNodes" class="com.hand.hap.core.impl.RedisNodeAutoConfig">
        <property name="sentinels" value="${redis.sentinel}"/>
    </bean>

    <!--這里添加的是Redis逐虚,因?yàn)槭褂玫氖荢pring里自帶的Redis的Session策略 -->
    <bean id="v2redisConnectionFactory" class="com.hand.hap.core.JedisConnectionFactoryBean">

        <property name="useSentinel" value="${redis.useSentinel}"/>

        <property name="sentinelConfiguration" ref="redisSentinelConfiguration"/>
        <property name="hostName" value="${redis.ip:localhost}"/>
        <property name="port" value="${redis.port:6379}"/>
        <property name="database" value="${redis.db:10}"/>
        <property name="password" value="${redis.password:}"/>
        <property name="poolConfig">
            <bean class="redis.clients.jedis.JedisPoolConfig">
                <property name="maxIdle" value="999"/>
                <property name="maxTotal" value="9999"/>
                <property name="minIdle" value="20"/>
            </bean>
        </property>
    </bean>

    <bean id="stringRedisSerializer"
          class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

    <bean id="v2redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="v2redisConnectionFactory"/>
        <property name="keySerializer" ref="stringRedisSerializer"/>
        <property name="valueSerializer" ref="stringRedisSerializer"/>
        <property name="hashKeySerializer" ref="stringRedisSerializer"/>
        <property name="hashValueSerializer" ref="stringRedisSerializer"/>
    </bean>
    <!-- 這里的是為了下面的 Session策略過(guò)濾器提供構(gòu)造函數(shù)傳入的參數(shù),因?yàn)镾ession過(guò)濾器要依賴該對(duì)象來(lái)構(gòu)造谆膳,所以創(chuàng)建一個(gè)先 -->
    <bean name="redisOperationsSessionRepository"
          class="org.springframework.session.data.redis.RedisOperationsSessionRepository">
        <constructor-arg ref="v2redisConnectionFactory"/>
        <property name="defaultMaxInactiveInterval" value="${session.expire.time:3600}"/>
    </bean>

    <!-- 這個(gè)是Session策略過(guò)濾器叭爱,即將容器原有的Session持久化機(jī)制,代替為Spring的 Redis持久化Session機(jī)制漱病。 -->
    <!-- 注意买雾,這個(gè)名字與 web.xml里的targetBean的下value是要一致的。 -->
    <bean name="springSession" class="org.springframework.session.web.http.SessionRepositoryFilter">
        <constructor-arg ref="redisOperationsSessionRepository"/>
        <property name="httpSessionStrategy" ref="cookieHttpSessionStrategy"/>
    </bean>
</beans>

大概就是 SessionRepositoryFilter 攔截所有請(qǐng)求杨帽,springSessionRepositoryFilter替換容器(Tomcat)默認(rèn)的HttpSession支持為Spring Session漓穿,將Session實(shí)例存放在Redis中∽⒂可以看看它的關(guān)鍵代碼

@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

        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 {
            filterChain.doFilter(strategyRequest, strategyResponse);
        }
        finally {
            wrappedRequest.commitSession();
        }
    }
    ......
}
  • SessionRepositoryRequestWrapper是HttpServletRequest的子類晃危,接管原來(lái)的request,重寫(xiě)了與session相關(guān)的方法老客。
  • SessionRepositoryResponseWrapper是HttpServletResponse的子類僚饭,接管原來(lái)的response。

因?yàn)镾essionRepositoryRequestWrapper繼承了HttpServletRequestWrapper胧砰,而HttpServletRequestWrapper實(shí)現(xiàn)了HttpServletRequest接口鳍鸵,在SessionRepositoryRequestWrapper又重寫(xiě)了HttpServletRequest接口中的一些方法,所以才會(huì)有:getSession尉间、changeSessionId等這些方法偿乖。
到此,我們應(yīng)該大致明白了乌妒,原有的request請(qǐng)求和response都被重新進(jìn)行了包裝汹想。我們也就明白了原有的HttpSeesion是如何被Spring Session替換掉的。

image.png

可以通過(guò)繼承HttpServletRequestWrapper 達(dá)到修飾 HttpServletRequest 的目的撤蚊。
同理古掏,HttpServletResponseWrapper 類似。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侦啸,一起剝皮案震驚了整個(gè)濱河市槽唾,隨后出現(xiàn)的幾起案子丧枪,更是在濱河造成了極大的恐慌,老刑警劉巖庞萍,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拧烦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钝计,警方通過(guò)查閱死者的電腦和手機(jī)恋博,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)私恬,“玉大人债沮,你說(shuō)我怎么就攤上這事”久” “怎么了疫衩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)荣德。 經(jīng)常有香客問(wèn)我闷煤,道長(zhǎng),這世上最難降的妖魔是什么涮瞻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任鲤拿,我火速辦了婚禮,結(jié)果婚禮上饲宛,老公的妹妹穿的比我還像新娘皆愉。我一直安慰自己,他們只是感情好艇抠,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布幕庐。 她就那樣靜靜地躺著,像睡著了一般家淤。 火紅的嫁衣襯著肌膚如雪异剥。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天絮重,我揣著相機(jī)與錄音冤寿,去河邊找鬼。 笑死青伤,一個(gè)胖子當(dāng)著我的面吹牛督怜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狠角,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼号杠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起姨蟋,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屉凯,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后眼溶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體悠砚,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年堂飞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灌旧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绰筛,死狀恐怖节榜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情别智,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布稼稿,位于F島的核電站薄榛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏让歼。R本人自食惡果不足惜敞恋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谋右。 院中可真熱鬧硬猫,春花似錦、人聲如沸改执。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辈挂。三九已至衬横,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間终蒂,已是汗流浹背蜂林。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拇泣,地道東北人噪叙。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像霉翔,于是被迫代替她去往敵國(guó)和親睁蕾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容