zoukankan      html  css  js  c++  java
  • 【Spring】Spring Session的简单搭建与源码阅读

    搭建一个简单的Spring Session例子

    引入依赖包

        <dependencies>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-redis</artifactId>
                <version>1.7.10.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>2.8.2</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session</artifactId>
                <version>1.2.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>4.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>4.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>4.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
            </dependency>
            <dependency>
                <groupId>biz.paluch.redis</groupId>
                <artifactId>lettuce</artifactId>
                <version>3.5.0.Final</version>
            </dependency>
        </dependencies>
    

    注册Spring IoC、Spring Session和一些Servlet

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        id="WebApp_ID" version="2.5">
        <display-name>Spring-Session-Redis</display-name>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                    classpath*:spring-session.xml
                </param-value>
        </context-param>
    
        <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>
        </filter-mapping>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <servlet>
            <description></description>
            <display-name>SessionTestServlet</display-name>
            <servlet-name>SessionTestServlet</servlet-name>
            <servlet-class>com.nicchagil.SessionTestServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>SessionTestServlet</servlet-name>
            <url-pattern>/SessionTestServlet</url-pattern>
        </servlet-mapping>
        
    </web-app>
    

    最简单的Spring Session的Bean配置

    <?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"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans    
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
        http://www.springframework.org/schema/context   
        http://www.springframework.org/schema/context/spring-context-3.0.xsd 
        http://www.springframework.org/schema/mvc   
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
        
        <context:annotation-config />
    
        <!-- Jedis连接工厂 -->
        <bean id="jedisConnectionFactory"
            class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
            <property name="hostName" value="nick-huang.example" />
            <property name="port" value="6379" />
        </bean>
    
        <bean id="redisHttpSessionConfiguration"
            class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
            <property name="maxInactiveIntervalInSeconds" value="1800" />
        </bean>
    
    </beans>
    

    一个测试的Servlet

    package com.nicchagil;
    
    import java.io.IOException;
    import java.util.logging.Logger;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    /**
     * Servlet implementation class SessionTestServlet
     */
    public class SessionTestServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
        private Logger logger = Logger.getLogger("SessionTestServlet");
    
        /**
         * Default constructor. 
         */
        public SessionTestServlet() {
            // TODO Auto-generated constructor stub
        }
    
        /**
         * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
         */
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            HttpSession session = request.getSession();
            
            String username = request.getParameter("username");
            if (username != null && username.length() > 0) {
                session.setAttribute("username", username);
            }
            
            response.getWriter().append("Served at: ").append(request.getContextPath()).append(", userName : " + session.getAttribute("username"));
        }
    
        /**
         * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
         */
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // TODO Auto-generated method stub
            doGet(request, response);
        }
    
    }
    

    测试

    启动,用浏览器访问该Servlet:http://127.0.0.1:8080/SessionTestServlet?username=123,然后不带参数可能获取用户名:http://127.0.0.1:8080/SessionTestServlet。

    看下Redis,有没有持久化Session:

    [root@blog ~]# /opt/redis-3.2.1/src/redis-cli 
    127.0.0.1:6379> keys *
    1) "spring:session:sessions:expires:5b29c067-a4b1-4d51-98b2-be084703fc78"
    2) "spring:session:sessions:5b29c067-a4b1-4d51-98b2-be084703fc78"
    3) "spring:session:expirations:1497195000000"
    

    接下来看下实现原理。

    委托过滤器代理类,DelegatingFilterProxy

    这个类不在Spring Session中,但它是我们上述例子配置的入口。
    它是委托过滤器代理类,可以看到它的继承与实现关系:DelegatingFilterProxy -> GenericFilterBean -> Filter,其中GenericFilterBean的init()调用DelegatingFilterProxy的initFilterBean()。

    initFilterBean()的作用是获取委托的过滤器,并调用委托过滤器的doFilter(),这个过滤器是springSessionRepositoryFilter

    Spring Session主要配置类,RedisHttpSessionConfiguration

    说到springSessionRepositoryFilter,那么它在哪里实例化的呢?我们先看看RedisHttpSessionConfiguration
    RedisHttpSessionConfiguration,这是一个配置类,它注册了一些Spring Session所需的Bean。我们通过以下的xml显式注册一个配置Bean:

        <bean id="redisHttpSessionConfiguration"
            class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
            <property name="maxInactiveIntervalInSeconds" value="1800" />
        </bean>
    

    这个类的继承关系是:RedisHttpSessionConfiguration -> SpringHttpSessionConfigurationSpringHttpSessionConfiguration有个方法叫springSessionRepositoryFilter,这里就是注册springSessionRepositoryFilterBean的地方。关于@Configuration、@Bean方式注册Bean,请点击这里

    Session存储过滤器,SessionRepositoryFilter

    它的继承关系是SessionRepositoryFilter -> OncePerRequestFilter -> Filter
    OncePerRequestFilter的名字说明了其作用,看doFilter方法可知,它通过request中的一个Attribute判断是否已做过滤,以保证对于每次请求只做一次此过滤逻辑。如果此请求是首次进入此过滤,则调用SessionRepositoryFilter.doFilterInternal

    SessionRepositoryFilter.doFilterInternal,将Servlet容器传入的Request和Response包装成自己封装的Request和Response,然后传给下一任Filter,后续的Filter和Servlet都使用Spring Session封装的Request和Response,此Request和Response分别继承HttpServletRequestWrapper、HttpServletResponseWrapper,并作了自己业务的覆盖。

    HTTP请求包装类,SessionRepositoryRequestWrapper

    SessionRepositoryRequestWrapper根据自身业务,覆盖了许多方法,这里不多讨论,简单举例,比如getSession(boolean)

    commitSession()

    Http请求与Session的关系策略,HttpSessionStrategy

    HttpSessionStrategy接口定义了几个方法,另外有几个实现类,这里只讨论一部分:

    // 从request获取请求的SessionID,比如SessionID有可能放在Cookie或请求头中
    String getRequestedSessionId(HttpServletRequest request);
    
    // 当新Session被创建且应通知客户端新SessionID时此方法会被调用。此方法的实现可能为Cookie或响应头设置新SessionID,当然也可以设置其他信息
    void onNewSession(Session session, HttpServletRequest request,
            HttpServletResponse response);
    
    // 当Session销毁时且需通知客户端该SessionID不再有效时会调用此方法。此方法的实现可能为从Cookie或响应头移除SessionID。
    void onInvalidateSession(HttpServletRequest request, HttpServletResponse response);
    

    Cookie方式,CookieHttpSessionStrategy

    getRequestedSessionId:

    onNewSession:

    onInvalidateSession:

    HTTP请求头方式,HeaderHttpSessionStrategy

    HeaderHttpSessionStrategy相对简单,根据头键值x-auth-token,从请求中读取sessionID,或设置响应头。
    值得注意的是,销毁Session时onInvalidateSession设置响应头x-auth-token的值为空。

    Session持久化,SessionRepository

    这是持久化Session的接口,定义有几个方法:

    // 创建能被此实现持久化的新Session。
    S createSession();
    
    // 持久化Session
    void save(S session);
    
    // 根据ID查询Session
    S getSession(String id);
    
    // 删除Session
    void delete(String id);
    
  • 相关阅读:
    [转] packagelock.json
    前端框架和技术
    typescript
    微信小程序登陆流程
    Introduction to my galaxy engine 4: Test on local light model
    Introduction to my galaxy engine 3: Local light model
    Introduction to my galaxy engine 5: Differed Lighting
    Introduction to my galaxy engine 2: Depth of field
    自己整理的一些国外免费3D模型网站,以后还会陆续添加
    Introduction to my galaxy engine 6: Differed Lighting 2
  • 原文地址:https://www.cnblogs.com/nick-huang/p/6986824.html
Copyright © 2011-2022 走看看