1. 程式人生 > >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("<(.*)>");


    }

    /**
     * 獲取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>