我的编程空间,编程开发者的网络收藏夹
学习永远不晚

spring-session自定义序列化方式

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

spring-session自定义序列化方式

spring-session自定义序列化

spring-session默认采用jdk序列化方法,该方法效率低下、内存占用大,且需要额外修改代码。故需要自定义序列化方法

自定义序列方法使用jackson库

首先需要一个类作为序列化的工具,需要实现

RedisSerializer

该接口在反序列化时没有提供对应的class对象,因此使用jackson反序列化时,都会返回成Object对象

因此我的解决思路是,在序列化时,获取对应bean的class,将其和bean序列化后的结果一并返回,存入redis

反序列化时,首先将byte数组转化成字符串,在从中截取存入的class字符串,作为参数传入jackson反序列化方法中

问题:对于带有泛型的bean,无法将其转化成真正适合的类型

解决方案:对于list,map,set等集合类,获取其第一个元素的class,存入redis

缺点:要求集合类元素必须是同一个子类,不能来自于同一个父类

问题:spring-session在删除attribute时,并没有真正从redis中删除,只是将value置为null,此时也会调用该类做序列化

解决方案:查看spring-session源码得知,对于null值得处理方法为直接返回一个个数为0的byte数组,反序列化时直接返回null即可


import cn.nsu.edu.web.four.config.BaseStatic;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.data.redis.serializer.SerializationUtils;
 
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class SessionSerializer implements RedisSerializer<Object> {
    @Autowired
    private ObjectMapper mapper;
    private Logger logger = LoggerFactory.getLogger(getClass());
    private final String separator = "=";
    private final String classPrefix = "<";
    private final String classSuffix = ">";
    private final String classSeparator = ","; 
    private Pattern pattern; 
    public SessionSerializer() {
        pattern = Pattern.compile("<(.*)>");  
    }
 
    
    private String getBegin(Object obj) {
        StringBuilder builder = new StringBuilder(obj.getClass().toString().substring(6) + classPrefix);
        if (obj instanceof List) {
            List list = ((List) obj);
            if (!list.isEmpty()) {
                Object temp = list.get(0);
                builder.append(temp.getClass().toString().substring(6));
            }
        } else if (obj instanceof Map) {
            Map map = ((Map) obj);
            Iterator iterator = map.keySet().iterator();
            if (iterator.hasNext()) {
                Object key = iterator.next();
                Object value = map.get(key);
                builder.append(key.getClass().toString().substring(6)).append(classSeparator).append(value.getClass().toString().substring(6));
            }
        } else if (obj instanceof Set) {
            Set set = ((Set) obj);
            Iterator iterator = set.iterator();
 
            if (iterator.hasNext()) {
                Object value = iterator.next();
                builder.append(value.getClass().toString().substring(6));
            }
        }
        builder.append(classSuffix);
        return builder.toString();
    }
 
    @Override
    public byte[] serialize(Object o) throws SerializationException {
        if (o == null)
            return new byte[0];
        try {
            String builder = getBegin(o) +
                    separator +
                    mapper.writeValueAsString(o);
            return builder.getBytes(BaseStatic.CHARSET);
        } catch (UnsupportedEncodingException | JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) return null;//已被删除的session
        try {
            String temp = new String(bytes, BaseStatic.CHARSET); 
            String cl[] = getClass(temp); 
            if (cl == null) {
                throw new RuntimeException("错误的序列化结果=" + temp);
            }
            if (cl.length == 1) {
                return mapper.readValue(temp.substring(temp.indexOf(separator) + 1), Class.forName(cl[0]));
            } else if (cl.length == 2) {
                TypeFactory factory = mapper.getTypeFactory();
                JavaType type = factory.constructParametricType(Class.forName(cl[0]), Class.forName(cl[1]));
                return mapper.readValue(temp.substring(temp.indexOf(separator) + 1), type);
            } else if (cl.length == 3) {
                TypeFactory factory = mapper.getTypeFactory();
                JavaType type = factory.constructParametricType(Class.forName(cl[0]), Class.forName(cl[1]), Class.forName(cl[2]));
                return mapper.readValue(temp.substring(temp.indexOf(separator) + 1), type);
            }
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    
    private String[] getClass(String value) {
        int index = value.indexOf(classPrefix);
        if (index != -1) {
            Matcher matcher = pattern.matcher(value.subSequence(index, value.indexOf(classSuffix) + 1));
            if (matcher.find()) {
                String temp = matcher.group(1);
                if (temp.isEmpty()) {//没有泛型
                    return new String[]{value.substring(0, index)};
                } else if (temp.contains(classSeparator)) {//两个泛型
                    int nextIndex = temp.indexOf(classSeparator);
                    return new String[]{
                            value.substring(0, index),
                            temp.substring(0, nextIndex),
                            temp.substring(nextIndex + 1)
                    };
                } else {//一个泛型
                    return new String[]{
                            value.substring(0, index),
                            temp
                    };
                }
            }
        }
        return null;
     }
}

配置spring-session序列化

在之前的配置文件上进行修改


 <bean id="springSessionDefaultRedisSerializer" class="cn.nsu.edu.web.four.session.serializer.SessionSerializer"/>
    <bean id="jedisConnectionFactory"
          class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
        <property name="hostName" value="${redis.host}"/>
        <property name="port" value="${redis.port}"/>
        <property name="timeout" value="${redis.timeout}"/>
        <property name="password" value="${redis.password}"/>
        <property name="database" value="${redis.database}"/>
        <property name="usePool" value="true"/>
        <property name="poolConfig" ref="redisPoolConfig"/> 
    </bean> 
 
    <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory"/>
        <property name="defaultSerializer" ref="springSessionDefaultRedisSerializer"/>
        <!--指定序列化类-->
        <property name="valueSerializer" ref="springSessionDefaultRedisSerializer"/>
        <property name="hashValueSerializer" ref="springSessionDefaultRedisSerializer"/>
    </bean>

spring-session序列化问题排查

严重: Servlet.service() for servlet [spring] in context with path [/] threw exception
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.mogoroom.service.vo.criteria.QueryBSPromotionListVO]
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:52)
at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:146)
at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:128)
at org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:85)
at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:778)
at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:670)
at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:388)
at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:245)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:245)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:217)
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:170)
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:80)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.mogoroom.service.vo.criteria.QueryBSPromotionListVO]
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:67)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:34)
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:50)
... 29 more
Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.mogoroom.service.vo.criteria.QueryBSPromotionListVO]
at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:41)
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:62)
... 31 more

问题

spring session 异常信息没有打印到日志中 用是默认jdk序列化。由于实体没有序列话,导致异常,但是没有输入到日志,导致定位到问题。

在代码中 request.getSession().setAttribute()是不会出现异常的 spring session 一次请求返回的时候,才会commit,才会触发spring session提交。

代码如下:onResponseCommitted


 
 private final class SessionRepositoryResponseWrapper
   extends OnCommittedResponseWrapper { 
  private final SessionRepositoryRequestWrapper request;
 
  
  SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request,
    HttpServletResponse response) {
   super(response);
   if (request == null) {
    throw new IllegalArgumentException("request cannot be null");
   }
   this.request = request;
  }
 
  @Override
  protected void onResponseCommitted() {
   this.request.commitSession();
  }
 } 
 
OnCommittedResponseWrapper 
abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
 
  
 private void doOnResponseCommitted() {
  if (!this.disableOnCommitted) {
   onResponseCommitted();
   disableOnResponseCommitted();
  }
 } 
} 

doOnResponseCommitted相关依赖出发方法

解决方法

filter抓下日志


chain.doFilter(wrappedRequest, response); 
}catch (Exception ex){
logger.error("xxf",ex);
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

spring-session自定义序列化方式

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

spring-session自定义序列化方法是什么

本篇内容介绍了“spring-session自定义序列化方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!spring-session
2023-06-22

Redis中怎么自定义序列化方法

在Redis中,可以通过自定义序列化方法来对存储的数据进行序列化和反序列化。一种常见的方式是使用JSON格式来进行序列化,可以通过以下步骤实现自定义序列化方法:创建一个自定义的序列化方法,例如可以使用JSON格式来序列化数据。可以使用Pyt
Redis中怎么自定义序列化方法
2024-04-29

MySQL自定义序列数的实现方式

目录mysql自定义序列数实现创建序列表插入定义的序列自定义函数实现MyBATis中调用函数来获取最新序列数MySQL添加自定义的序列使用实例总结MySQL自定义序列数实现往往有很多情况下,我们需要使用自己生成的唯一Id或保证不重复的序列
2022-12-28

如何自定义Go Json的序列化方法

这篇文章主要讲解了“如何自定义Go Json的序列化方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何自定义Go Json的序列化方法”吧!我们知道,通过tag,可以有条件地实现定制Go
2023-07-02

fastjson序列化时间自定义格式示例详解

这篇文章主要为大家介绍了fastjson序列化时间自定义格式示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-18

Spring session redis 修改默认的序列化方法(案例)

这篇文章主要介绍了Spring session redis 修改默认的序列化方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-05-14

Flask自定义序列化超详细讲解

序列化其实就是将数据转化成一种可逆的数据结构,自然,逆向的过程就叫做反序列化。php将数据序列化和反序列化会用到两个函数:serialize将对象格式化成有序的字符串、unserialize将字符串还原成原来的对象
2022-11-13

Redis中如何实现自定义序列化器

在Redis中实现自定义序列化器需要使用Redis的自定义模块功能。Redis的自定义模块功能允许用户编写自定义的功能模块,并在Redis中加载和使用这些模块。以下是一种可能的实现方式:编写一个自定义模块,实现自定义的序列化器函数。可以使
Redis中如何实现自定义序列化器
2024-04-29

python魔法方法-自定义序列详解

自定义序列的相关魔法方法允许我们自己创建的类拥有序列的特性,让其使用起来就像 python 的内置序列(dict,tuple,list,string等)。 如果要实现这个功能,就要遵循 python 的相关的协议。所谓的协议就是一些约定内容
2022-06-04

Gson Java 是否可以自定义反序列化忽略字段?(Gson Java能自定义反序列化忽略字段吗)

在Java开发中,Gson是一个非常常用的JSON解析库,它提供了便捷的方式来将Java对象与JSON数据进行相互转换。其中,反序列化是将JSON数据转换为Java对象的过程。那么,GsonJava能否自定义反序列化忽略字段呢?这是一个在开发过程中经常会遇到的问题
Gson Java 是否可以自定义反序列化忽略字段?(Gson Java能自定义反序列化忽略字段吗)
Java2024-12-18

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录