【Spring Boot 3 + fastjson2】更改RedisTemplate的序列化策略

环境:jdk17、Spring Boot 3.1.3、fastjson2 2.0.41

pom依赖

<properties>
    <java.version>17</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>3.1.3</spring-boot.version>
    <fastjson2.version>2.0.41</fastjson2.version>
<properties>
<dependencies>
	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2-extension-spring6</artifactId>
        <version>${fastjson2.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Jackson2Json

在查找资料的过程中,RedisTemplate的序列化策略大部分是用jackson去实现的,这里我也在这里实现以下

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisTemplateConfig {

    @Resource
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        // 自定义 String Object
        RedisTemplate<String, Object> template = new RedisTemplate<>();

        // ObjectMapper 转译
        ObjectMapper objectMapper = createObjectMapper();

        // Json 序列化配置
        Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);

        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key 采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash 的key也采用 String 的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value 序列化方式采用 jackson
        template.setValueSerializer(objectJackson2JsonRedisSerializer);
        // hash 的 value 采用 jackson
        template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
        template.setConnectionFactory(redisConnectionFactory);
        template.afterPropertiesSet();

        return template;
    }

    private ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY
        );
        return mapper;
    }
}

如果key不采用stringRedisSerializer方式的话,存储的key会有双引号(“”)而且无法通过key去get到value

Tips:

new Jackson2JsonRedisSerializer<>(objectMapper, Object.class)这个方法是3.0才有的,不是3.0的话使用objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper)才行,到3.0objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper)这个方法已经过时了,所以采用new Jackson2JsonRedisSerializer<>(objectMapper, Object.class)这个方法比较好。

fastjson2

查看Jackson2JsonRedisSerializer的源码知道这个类是实现了RedisSerializer<>的,所以我们也要实现。

先定义一个Fastjson2RedisSerializer并实现RedisSerializer<>:

public class FastJson2RedisSerializer<T> implements RedisSerializer<T> {}

然后去实现方法,这里加快速度,直接展示成品

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

public class FastJson2RedisSerializer<T> implements RedisSerializer<T> {

    static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(
            // 按需加上需要支持自动类型的类名前缀,范围越小越安全
            "com.***.***"
    );

    private final Class<T> clazz;

    public FastJson2RedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        return JSON.toJSONBytes(t, JSONWriter.Feature.WriteClassName);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (ArrayUtils.isEmpty(bytes)) {
            return null;
        }

        return JSON.parseObject(bytes, clazz, AUTO_TYPE_FILTER);
    }
}

然后再去配置类中实现我们的fastjson2的序列化配置

import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisTemplateConfig {

    @Resource
    private RedisConnectionFactory redisConnectionFactory;
    
        @Bean
    public RedisTemplate<Object, Object> redisTemplate() {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        FastJson2RedisSerializer<Object> fastJson2RedisSerializer = new FastJson2RedisSerializer<>(Object.class);
        StringRedisSerializer serializer = new StringRedisSerializer();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(serializer);
        template.setHashKeySerializer(serializer);
        template.setValueSerializer(fastJson2RedisSerializer);
        template.setHashValueSerializer(fastJson2RedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

优化

FastJson2RedisSerializer

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.util.Objects;

@Slf4j
public class FastJson2RedisSerializer<T> implements RedisSerializer<T> {

    static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(
            // 按需加上需要支持自动类型的类名前缀,范围越小越安全
            "com.***.***"
    );

    private final Class<T> clazz;

    public FastJson2RedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (Objects.isNull(t)) {
            return new byte[0];
        }
        try {
            return JSON.toJSONBytes(t, JSONWriter.Feature.WriteClassName);
        } catch (Exception e) {
            log.error("Fastjson2 序列化错误:{}", e.getMessage());
            throw new SerializationException("无法序列化: " + e.getMessage(), e);
        }

    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (ArrayUtils.isEmpty(bytes)) {
            return null;
        }
        try {
            return JSON.parseObject(bytes, clazz, AUTO_TYPE_FILTER);
        } catch (Exception e) {
            log.error("Fastjson2 反序列化错误:{}", e.getMessage());
            throw new SerializationException("无法反序列化: " + e.getMessage(), e);
        }

    }
}
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,在Spring Boot项目中添加RedisFastjson的依赖: ```xml <!-- Redis依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Fastjson依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.73</version> </dependency> ``` 接着,在application.yml中配置Redis连接信息: ```yaml spring: redis: host: localhost port: 6379 password: database: 0 ``` 然后,我们可以创建一个RedisUtil类来操作Redis,示例如下: ```java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component public class RedisUtil { @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 添加缓存 * @param key * @param value * @param expireTime */ public void set(String key, Object value, long expireTime) { if (value instanceof String) { redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS); } else { redisTemplate.opsForValue().set(key, JSON.toJSONString(value, SerializerFeature.WriteClassName), expireTime, TimeUnit.SECONDS); } } /** * 获取缓存 * @param key * @param clazz * @param <T> * @return */ public <T> T get(String key, Class<T> clazz) { Object value = redisTemplate.opsForValue().get(key); return value == null ? null : JSON.parseObject(value.toString(), clazz); } /** * 删除缓存 * @param key */ public void delete(String key) { redisTemplate.delete(key); } } ``` 在上面的代码中,我们使用了Fastjson对对象进行序列化和反序列化,并且在set方法中判断了value的类型,如果是String类型,则直接存储,否则使用Fastjson将对象序列化后再存储。在get方法中,我们先获取缓存的值,然后判断是否为null,如果不为null,则使用Fastjson将值反序列化成指定的class类型。 最后,在需要使用Redis的地方,我们可以注入RedisUtil,然后调用它的方法即可: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @Autowired private RedisUtil redisUtil; @GetMapping("/test") public String test() { String key = "test"; String value = "hello world"; redisUtil.set(key, value, 60); String result = redisUtil.get(key, String.class); return result; } } ``` 以上就是在Spring Boot项目中集成Redis并使用Fastjson进行序列化的示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值