Spring MVC使用redis共享session

2018-05-07 15:49:39 浏览数 (1)

Spring MVC

Spring Web MVC是基于Java的轻量级Web框架,使用了MVC架构模式的思想。Spring Web MVC核心架构为:

  1. 用户发送的请求到达前端控制器DispatcherServlet,前端控制器根据请求信息来决定使用哪一个页面控制器,并将处理请求转给该控制器。
  2. 页面控制器收到请求后,可以完成请求的逻辑(这里的逻辑复杂了),处理完毕后返回一个ModelAndView(模型数据和逻辑视图名)。
  3. 前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,渲染时会将返回的模型数据填充到视图中,即形成响应。
  4. 前端控制器将响应返回给用户。

示例应用

  1. 创建一个MAVEN的webapp项目,使用eclipse会默认生成需要的目录
  2. 通过tomcat可以部署该webapp项目,该项目的入口即为web.xml配置文件
代码语言:txt复制
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">
  <display-name>Archetype Created Web Application</display-name>
  
  <!-- 全局上下文的参数从classpath中的applicationContext.xml获取 -->
  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
      <listener-class>
          org.springframework.web.context.ContextLoaderListener
      </listener-class>
  </listener>
  
  <!-- 定义拦截session的过滤器 -->
  <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>
  
  <!-- 前端控制器 -->
  <servlet>
      <servlet-name>dispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring-mvc.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
      <servlet-name>dispatcherServlet</servlet-name>
      <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

spring-mvc.xml配置文件定义了前面提到的ModelAndView,只需要在classpath中能够找到即可,不固定文件位置。通常放在src/main/resources目录下:

代码语言:txt复制
<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
    <mvc:annotation-driven />
    <mvc:default-servlet-handler />
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
        p:viewClass="org.springframework.web.servlet.view.JstlView"
        p:prefix="/WEB-INF/views/"
        p:suffix=".jsp" />
    <context:component-scan base-package="sample" />
</beans>

全局的上下文配置文件applicationContext.xml主要用于Redis的连接配置,如下:

代码语言:txt复制
<?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:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                          http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    
    <context:component-scan base-package="sample" />
    
    <context:property-placeholder location="classpath:redis.properties"/>
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="maxTotal" value="${redis.maxActive}" />
        <property name="maxWaitMillis" value="${redis.maxWait}" />
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
    </bean>
    
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="poolConfig" ref="poolConfig" />
        <property name="port" value="${redis.port}" />
        <property name="hostName" value="${redis.host}" />
    </bean>
    
    <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
    </bean>
    
    <bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="maxInactiveIntervalInSeconds" value="1800" />
    </bean>
</beans> 

在上面的配置中有配置component-scan的base-package为sample,也就是搜索页面控制器的包。因此所有的页面控制器都需要放到该包下面。如:

代码语言:txt复制
package sample;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class AppController {
    @RequestMapping("/")
    public String home() {
        return "index";
    }
    
    @RequestMapping("/put")
    public String put(HttpSession session) {
        session.setAttribute("name", "test");
        return "index";
    }
    
    @RequestMapping("/get")
    @ResponseBody
    public String get(HttpSession session) {
        return (String) session.getAttribute("name");
    }
}

其中使用到的Controller和RequestMapping装饰器是必要的,否则无法处理请求逻辑。

测试&问题

  1. Redis服务未开启时,报错Could not get a resource from the pool。由于无法连接到Redis,因此可用连接耗完。
  2. Redis服务开启时,不支持config命令(可以通过配置文件rename-command屏蔽config命令),报错为:
代码语言:txt复制
Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent
Caused by: java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: ERR unknown command 'CONFIG'; nested exception is redis.clients.jedis.exceptions.JedisDataException: ERR unknown command 'CONFIG'

从报错信息中可以看出,由于enableRedisKeyspaceNotificationInitializer的报错而无法初始化,导致程序启动不了。而造成enableRedisKeyspaceNotificationInitializer报错的原因是无法使用CONFIG命令。解决方案:1)通过申请设置notify-keyspace-events为Egx;2)在配置文件中添加如下配置(这个配置和JedisConnectionFactory的放置在一起)

代码语言:txt复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:util="http://www.springframework.org/schema/util"
     xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> 
    <!-- 上面的两条util和schemaLocation需要填写,否则会报错util:constant没有匹配 -->
    <util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
</beans>

测试时,打开浏览器输入 http://localhost:8080/springsession/put 即可设置session,在Redis中可以查看到:

图1.png图1.png

然后通过 http://localhost:8080/springsession/get 请问的时候会发现,在请求cookie中会有:

图2.png图2.png

到此,spring session的简单使用和测试就已经完成了。网上对于这块的资料很杂,写法也有很多种,这里只是使用了其中一种来进行测试。

参考

  • http://jinnianshilongnian.iteye.com/blog/1594806
  • http://blog.csdn.net/xiao__gui/article/details/52706243
  • https://github.com/13babybear/bounter-springsession

0 人点赞