本文介绍了“什么是spring-session的自定义序列化方法”的相关知识。很多人在实际办案过程中都会遇到这样的困难。接下来,让边肖带领大家学习如何应对这些情况!希望大家认真阅读,学点东西!
00-1010 Spring-Session默认采用jdk序列化方式,效率低下,占用大量内存,需要额外修改代码。因此,您需要自定义序列化方法。
00-1010首先需要一个类作为序列化的工具,这需要实现。
重串行器
反序列化时,此接口不提供相应的类对象,因此使用jackson反序列化时,它将作为对象对象返回。
因此,我的解决方案是在序列化过程中获取对应bean的类,将其与bean序列化的结果一起返回,并保存在redis中。
反序列化时,首先将字节数组转换为字符串,从中截取存储的类字符串并作为参数传递给jackson反序列化方法。
问题:无法将具有泛型的bean转换成真正合适的类型。
对于集合类如解决方案:列表、地图、集合等。获取其第一个元素的类,并将其存储在redis中。
缺点:要求集合类元素必须属于同一个子类,而不是来自同一个父类。
当问题:-session删除该属性时,它并没有真正从redis中删除它。它只是将值设置为null,这也将调用类进行序列化。
查看解决方案:, spring-session的源代码,我们知道null值的处理方法是直接返回一个编号为0的字节数组,反序列化时直接返回null。
import cn . nsu . edu . web . four . config . BaseStatic;
import com . fasterxml . Jackson . core . jsonprocessingexception;
import com . fasterxml . Jackson . data bind . JavaType;
import com . fasterxml . Jackson . data bind . object mapper;
import com . fasterxml . Jackson . data bind . type . TYPeFactory;
import org . slf4j . logger;
import org . slf4j . logger factory;
import org . spring framework . beans . factory . annotation . autowired;
import org . spring framework . data . redis . serializer . redis serializer;
import org . spring framework . data . redis . serializer . serialization exception;
import org . spring framework . data . redis . serializer . serializationutils;
import Java . io . ioexception;
import Java . io . unsupportedencodinegexception;
import Java . util . *;
import Java . util . regex . Matcher;
import Java . util . regex . pattern;
public class sessionserializerimplementredisserierobject {
@自动连线
privateObjectMappermapper
privateLoggerlogger=logger factory . getlogger(getClass());
privatefilestringseparator=“=”;
privatefilestringclassprefix=' ';
privatefinalStringclassSuffix=
">";
private final String classSeparator = ",";
private Pattern pattern;
public SessionSerializer() {
pattern = Pattern.compile("<(.*)>");
}
/**
* 获取class,包含集合类的泛型
* <p>暂只支持最多两个泛型,同时集合内数据必须为同一个实现类,不可将泛型声明成父类</p>
*
* @param obj 将要序列化的对象
* @return 没有泛型,形式为java.lang.String<>
* <p>一个泛型,形式为java.lang.String<java.lang.String></p>
* <p>两个个泛型,形式为java.lang.String<java.lang.String,java.lang.String></p>
*/
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;
}
/**
* 解析字符串,获取class
* <p>一个类型,java.lang.String<>={}</p>
* <p>两个类型,后面为泛型,java.lang.String<java.lang.String>={}</p>
* <p>三个类型,后面为泛型,java.lang.String<java.lang.String,java.lang.String>={}</p>
*
* @param value 包含class的字符串
* @return 返回所有类的数组
*/
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
/** * Allows ensuring that the session is saved if the response is committed. * * @author Rob Winch * @since 1.0 */ private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper { private final SessionRepositoryRequestWrapper request; /** * Create a new {@link SessionRepositoryResponseWrapper}. * @param request the request to be wrapped * @param response the response to be wrapped */ 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 { /** * Calls <code>onResponseCommmitted()</code> with the current contents as long as * {@link #disableOnResponseCommitted()} was not invoked. */ private void doOnResponseCommitted() { if (!this.disableOnCommitted) { onResponseCommitted(); disableOnResponseCommitted(); } } }
doOnResponseCommitted相关依赖出发方法
解决方法
filter抓下日志
chain.doFilter(wrappedRequest, response); }catch (Exception ex){ logger.error("xxf",ex); }
“spring-session自定义序列化方法是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/156063.html