Spring MVC使用redis共享session

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配置文件
<?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目录下:

<?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的连接配置,如下:

<?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,也就是搜索页面控制器的包。因此所有的页面控制器都需要放到该包下面。如:

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命令),报错为:
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的放置在一起)

<?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

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

图2.png

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

参考

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java架构师

storm基础系列之五---------接入数据收集系统flume

1.基本结构介绍   flume是三层架构,agent,collector,storage。每一层都可水平扩展。   其中,agent就是数据采集方;colle...

3039
来自专栏Spring相关

第5章—构建Spring Web应用程序—SpringMVC详解

第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)

1654
来自专栏王磊的博客

Spring Boot 最佳实践(二)集成Jsp与生产环境部署

提起Java不得不说的一个开发场景就是Web开发,也是Java最热门的开发场景之一,说到Web开发绕不开的一个技术就是JSP,因为目前市面上仍有很多的公司在使用...

2505
来自专栏Hadoop实操

11.如何为CDSW集成RedHat7的OpenLDAP认证

在前面的文章中Fayson介绍了CDH各个组件与OpenLDAP的集成,具体可以看Fayson更新的《从入门到精通 - Fayson带你玩转CDH》文章里面包含...

1223
来自专栏哎_小羊

Eclipse(Luna)集成Resin4.0+服务器,以及配置参数

当前,作为比较流行的web服务器,tomcat一直作为首选,然而近几年一个号称最快的Jsp、Servlet服务器——Resin出现了,Resin作为一个新秀We...

21210
来自专栏Ryan Miao

Ubuntu安装Java8和Java9

前言 系统:Ubuntu 16.04 软件: Java8, Java9 Tips: Java 9 的代码由于提供了新特性,所以有些代码并不支持向后兼容。也就...

4198
来自专栏Java 源码分析

SpringCloud:Eureka服务注册与发现

Eureka 其实就是一个 服务注册与发现的中心,也就是相当于我们前面做的一些生产者的服务需要注册到我们的注册中心,那么我们的消费者就不用把代码写死,而是可以去...

1883
来自专栏好好学java的技术栈

玩转springboot:日志的使用

以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法; 给系统里面导入slf4j的jar和 logback的实现jar...

2662
来自专栏玩转JavaEE

JavaWeb之最简洁的配置实现文件上传

按:最近公众号文章主要是整理一些老文章,主要是个人CSDN上的博客,也会穿插一些新的技术点。 ---- Spring、SpringMVC持续介绍中,基础配置前面...

2803
来自专栏JAVA烂猪皮

Spring核心——Bean的定义与控制

在Sring核心与设计模式的文章中,分别介绍了Ioc容器和Bean的依赖关系。如果阅读过前2文就会知道,Spring的整个运转机制就是围绕着IoC容器以及Bea...

1001

扫码关注云+社区

领取腾讯云代金券