java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot Redis序列化

SpringBoot整合Redis实现序列化的7种策略详解

作者:风象南

在分布式系统中,Redis作为高性能的缓存和数据存储解决方案被广泛应用,本文将介绍SpringBoot中Redis的7种序列化策略,有需要的小伙伴可以根据需求进行选择

在分布式系统中,Redis作为高性能的缓存和数据存储解决方案被广泛应用。当使用SpringBoot框架整合Redis时,序列化策略的选择直接影响到系统的性能、兼容性和可维护性。

序列化是将Java对象转换为可在网络上传输或存储在磁盘上的过程,反序列化则是将这些数据重新转换为Java对象。

合适的序列化策略能够带来以下好处:

本文将介绍SpringBoot中Redis的7种序列化策略

1. JdkSerializationRedisSerializer

1.1 原理介绍

JdkSerializationRedisSerializer是Spring Data Redis默认的序列化策略,它使用Java原生的序列化机制(java.io.Serializable)将对象序列化为字节数组。这种方式要求被序列化的对象必须实现Serializable接口。

1.2 实现方式

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用JdkSerializationRedisSerializer对value进行序列化
        template.setValueSerializer(new JdkSerializationRedisSerializer());
        
        // 使用StringRedisSerializer对key进行序列化
        template.setKeySerializer(new StringRedisSerializer());
        
        template.afterPropertiesSet();
        return template;
    }
}

使用示例:

@Service
public class UserService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void saveUser(User user) {
        // User类必须实现Serializable接口
        redisTemplate.opsForValue().set("user:" + user.getId(), user);
    }
    
    public User getUser(Long id) {
        return (User) redisTemplate.opsForValue().get("user:" + id);
    }
}

1.3 优缺点分析

优点

缺点

适用场景

2. StringRedisSerializer

2.1 原理介绍

StringRedisSerializer是最简单的序列化器,它直接将String类型的数据按照指定的字符集(默认UTF-8)编码为字节数组。由于其简单高效的特性,它通常用于Redis的key序列化,也适用于value为String类型的场景。

2.2 实现方式

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, String> stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(connectionFactory);
        return template;
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 对key使用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        
        // 对value可以使用其他序列化器
        // ...
        
        template.afterPropertiesSet();
        return template;
    }
}

使用示例:

@Service
public class CacheService {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    public void saveString(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }
    
    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }
}

2.3 优缺点分析

优点

缺点

适用场景

3. Jackson2JsonRedisSerializer

3.1 原理介绍

Jackson2JsonRedisSerializer使用Jackson库将对象序列化为JSON格式的字符串。它能处理大多数常见的Java对象,并产生人类可读的序列化结果,同时提供了较好的性能和压缩率。该序列化器需要指定序列化的目标类型。

3.2 实现方式

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, User> userRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, User> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 对key使用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        
        // 对User对象使用Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<>(User.class);
        
        // 配置ObjectMapper以增强序列化功能
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, 
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);
        
        template.setValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
}

使用示例:

@Service
public class UserCacheService {
    
    @Autowired
    private RedisTemplate<String, User> userRedisTemplate;
    
    public void saveUser(User user) {
        userRedisTemplate.opsForValue().set("user:" + user.getId(), user);
    }
    
    public User getUser(Long id) {
        return userRedisTemplate.opsForValue().get("user:" + id);
    }
}

3.3 优缺点分析

优点

缺点

适用场景

4. GenericJackson2JsonRedisSerializer

4.1 原理介绍

GenericJackson2JsonRedisSerializer是Jackson2JsonRedisSerializer的增强版,它无需指定目标类型,能够处理任何类型的Java对象。它通过在JSON中嵌入类型信息来支持泛型和多态,使反序列化能够正确恢复对象类型。

4.2 实现方式

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 对key使用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        
        // 使用GenericJackson2JsonRedisSerializer进行序列化
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

使用示例

@Service
public class CacheService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 可以存储任何类型的对象
    public <T> void save(String key, T object) {
        redisTemplate.opsForValue().set(key, object);
    }
    
    // 无需进行类型转换
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> type) {
        return (T) redisTemplate.opsForValue().get(key);
    }
    
    // 存储泛型集合
    public <T> void saveList(String key, List<T> list) {
        redisTemplate.opsForValue().set(key, list);
    }
    
    // 获取泛型集合
    @SuppressWarnings("unchecked")
    public <T> List<T> getList(String key) {
        return (List<T>) redisTemplate.opsForValue().get(key);
    }
}

4.3 优缺点分析

优点

缺点

适用场景:

5. FastJsonRedisSerializer

5.1 原理介绍

FastJsonRedisSerializer基于阿里巴巴的FastJson库,FastJson是一个性能优越的JSON处理库,专为Java平台设计。它提供了极高的序列化和反序列化性能,在处理大量数据时尤其明显。

5.2 实现方式

首先,添加FastJson依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

创建自定义FastJson序列化器

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

    private final Class<T> clazz;
    
    public FastJsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        try {
            return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
        } catch (Exception ex) {
            throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
        }
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            String str = new String(bytes, StandardCharsets.UTF_8);
            return JSON.parseObject(str, clazz);
        } catch (Exception ex) {
            throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
        }
    }
}

配置RedisTemplate

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用StringRedisSerializer来序列化key
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        
        // 使用FastJsonRedisSerializer来序列化value
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        
        // 配置FastJson的特性
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        
        template.afterPropertiesSet();
        return template;
    }
}

使用示例

@Service
public class ProductService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void saveProduct(Product product) {
        redisTemplate.opsForValue().set("product:" + product.getId(), product);
    }
    
    public Product getProduct(Long id) {
        return (Product) redisTemplate.opsForValue().get("product:" + id);
    }
    
    public void saveProductList(List<Product> products) {
        redisTemplate.opsForValue().set("products:list", products);
    }
    
    @SuppressWarnings("unchecked")
    public List<Product> getProductList() {
        return (List<Product>) redisTemplate.opsForValue().get("products:list");
    }
}

5.3 优缺点分析

优点

缺点

适用场景

6. Kryo序列化

6.1 原理介绍

Kryo是一个快速高效的Java序列化框架,它产生的序列化结果非常紧凑,序列化和反序列化速度极快。

Kryo不仅比Java原生序列化快,而且比JSON序列化也快很多。它使用二进制格式,且支持对象图的处理(包括循环引用)。

6.2 实现方式

首先,添加Kryo依赖:

<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>5.3.0</version>
</dependency>

创建Kryo序列化器:

public class KryoRedisSerializer<T> implements RedisSerializer<T> {
    
    private static final byte[] EMPTY_ARRAY = new byte[0];
    
    private final Class<T> clazz;
    private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        kryo.setRegistrationRequired(false); // 关闭注册要求,自动注册类
        kryo.setReferences(true); // 支持循环引用
        return kryo;
    });
    
    public KryoRedisSerializer(Class<T> clazz) {
        this.clazz = clazz;
    }
    
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return EMPTY_ARRAY;
        }
        
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             Output output = new Output(baos)) {
            
            Kryo kryo = kryoThreadLocal.get();
            kryo.writeObject(output, t);
            output.flush();
            
            return baos.toByteArray();
        } catch (Exception e) {
            throw new SerializationException("Error serializing object using Kryo", e);
        }
    }
    
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        
        try (Input input = new Input(bytes)) {
            Kryo kryo = kryoThreadLocal.get();
            return kryo.readObject(input, clazz);
        } catch (Exception e) {
            throw new SerializationException("Error deserializing object using Kryo", e);
        }
    }
}

配置RedisTemplate:

@Configuration
public class RedisKryoConfig {
    
    @Bean
    public RedisTemplate<String, Object> kryoRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用StringRedisSerializer来序列化key
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        
        // 使用KryoRedisSerializer来序列化value
        KryoRedisSerializer<Object> kryoSerializer = new KryoRedisSerializer<>(Object.class);
        template.setValueSerializer(kryoSerializer);
        template.setHashValueSerializer(kryoSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

使用示例:

@Service
public class UserSessionService {
    
    @Autowired
    @Qualifier("kryoRedisTemplate")
    private RedisTemplate<String, Object> kryoRedisTemplate;
    
    public void saveUserSession(String sessionId, UserSession session) {
        kryoRedisTemplate.opsForValue().set("session:" + sessionId, session, 30, TimeUnit.MINUTES);
    }
    
    public UserSession getUserSession(String sessionId) {
        return (UserSession) kryoRedisTemplate.opsForValue().get("session:" + sessionId);
    }
    
    public void saveComplexObject(String key, ComplexObject object) {
        kryoRedisTemplate.opsForValue().set(key, object);
    }
    
    public ComplexObject getComplexObject(String key) {
        return (ComplexObject) kryoRedisTemplate.opsForValue().get(key);
    }
}

6.3 优缺点分析

优点

缺点

适用场景

7. Protocol Buffers (ProtoBuf)

7.1 原理介绍

Protocol Buffers(ProtoBuf)是由Google开发的一种语言无关、平台无关、可扩展的结构化数据序列化机制。它比XML更小、更快、更简单,适用于通信协议、数据存储等场景。

ProtoBuf使用预定义的消息格式,通过专用编译器生成代码,实现高效的序列化和反序列化。

7.2 实现方式

首先,添加Protocol Buffers依赖:

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.21.7</version>
</dependency>
<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java-util</artifactId>
    <version>3.21.7</version>
</dependency>

定义.proto文件 (src/main/proto/user.proto):

syntax = "proto3";

package com.example.model;
option java_package = "com.example.model.proto";
option java_outer_classname = "UserProto";

message User {
  int64 id = 1;
  string username = 2;
  string email = 3;
  int32 age = 4;
  
  enum UserType {
    NORMAL = 0;
    VIP = 1;
    ADMIN = 2;
  }
  
  UserType userType = 5;
  repeated string roles = 6;
  map<string, string> attributes = 7;
}

配置Maven插件生成Java代码:

<plugin>
    <groupId>org.xolstice.maven.plugins</groupId>
    <artifactId>protobuf-maven-plugin</artifactId>
    <version>0.6.1</version>
    <extensions>true</extensions>
    <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact>
        <pluginId>grpc-java</pluginId>
        <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
        <outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>
        <clearOutputDirectory>false</clearOutputDirectory>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

创建ProtoBuf序列化器:

public class ProtobufRedisSerializer<T extends MessageLite> implements RedisSerializer<T> {
    
    private final Class<T> clazz;
    private final Method parseFromMethod;
    
    public ProtobufRedisSerializer(Class<T> clazz) {
        this.clazz = clazz;
        try {
            this.parseFromMethod = clazz.getMethod("parseFrom", byte[].class);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Could not find parseFrom method on class " + clazz.getName());
        }
    }
    
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        try {
            return t.toByteArray();
        } catch (Exception e) {
            throw new SerializationException("Error serializing ProtoBuf message", e);
        }
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            return (T) parseFromMethod.invoke(null, bytes);
        } catch (Exception e) {
            throw new SerializationException("Error deserializing ProtoBuf message", e);
        }
    }
}

配置RedisTemplate:

@Configuration
public class RedisProtobufConfig {
    
    @Bean
    public RedisTemplate<String, UserProto.User> protobufRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, UserProto.User> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用StringRedisSerializer来序列化key
        template.setKeySerializer(new StringRedisSerializer());
        
        // 使用ProtobufRedisSerializer来序列化value
        ProtobufRedisSerializer<UserProto.User> protobufSerializer = new ProtobufRedisSerializer<>(UserProto.User.class);
        template.setValueSerializer(protobufSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

使用示例:

@Service
public class UserProtobufService {
    
    @Autowired
    private RedisTemplate<String, UserProto.User> protobufRedisTemplate;
    
    public void saveUser(UserProto.User user) {
        protobufRedisTemplate.opsForValue().set("proto:user:" + user.getId(), user);
    }
    
    public UserProto.User getUser(long id) {
        return protobufRedisTemplate.opsForValue().get("proto:user:" + id);
    }
    
    // 创建示例用户对象
    public UserProto.User createSampleUser(long id, String username) {
        return UserProto.User.newBuilder()
                .setId(id)
                .setUsername(username)
                .setEmail(username + "@example.com")
                .setAge(30)
                .setUserType(UserProto.User.UserType.VIP)
                .addRoles("USER")
                .addRoles("EDITOR")
                .putAttributes("department", "Engineering")
                .putAttributes("location", "Beijing")
                .build();
    }
}

将ProtoBuf对象转换为业务对象:

@Component
public class UserMapper {
    
    // ProtoBuf对象转换为业务对象
    public User protoToUser(UserProto.User protoUser) {
        User user = new User();
        user.setId(protoUser.getId());
        user.setUsername(protoUser.getUsername());
        user.setEmail(protoUser.getEmail());
        user.setAge(protoUser.getAge());
        
        // 转换枚举类型
        switch (protoUser.getUserType()) {
            case VIP:
                user.setUserType(UserType.VIP);
                break;
            case ADMIN:
                user.setUserType(UserType.ADMIN);
                break;
            default:
                user.setUserType(UserType.NORMAL);
        }
        
        // 转换列表
        user.setRoles(new ArrayList<>(protoUser.getRolesList()));
        
        // 转换Map
        user.setAttributes(new HashMap<>(protoUser.getAttributesMap()));
        
        return user;
    }
    
    // 业务对象转换为ProtoBuf对象
    public UserProto.User userToProto(User user) {
        UserProto.User.Builder builder = UserProto.User.newBuilder()
                .setId(user.getId())
                .setUsername(user.getUsername())
                .setEmail(user.getEmail())
                .setAge(user.getAge());
        
        // 转换枚举类型
        switch (user.getUserType()) {
            case VIP:
                builder.setUserType(UserProto.User.UserType.VIP);
                break;
            case ADMIN:
                builder.setUserType(UserProto.User.UserType.ADMIN);
                break;
            default:
                builder.setUserType(UserProto.User.UserType.NORMAL);
        }
        
        // 转换列表
        if (user.getRoles() != null) {
            builder.addAllRoles(user.getRoles());
        }
        
        // 转换Map
        if (user.getAttributes() != null) {
            builder.putAllAttributes(user.getAttributes());
        }
        
        return builder.build();
    }
}

7.3 优缺点分析

优点

缺点

适用场景:

总结对比

序列化方式序列化性能体积大小可读性跨语言复杂度类型信息向前兼容性
JDK完整
String极高极低
Jackson2Json
GenericJackson2Json中偏大完整
FastJson中偏小有限可选
Kryo极高极小有限
ProtoBuf极高极小极高完整

选型建议

根据不同的应用场景,以下是序列化策略的选择建议:

总结

选择合适的Redis序列化策略对于系统性能和兼容性至关重要。

最佳实践是针对不同类型的数据和不同的使用场景,以获得最佳的性能和开发体验。同时,应考虑版本控制、数据压缩等高级特性,为系统的长期演进做好准备。

通过合理选择和配置Redis序列化策略,可以显著提升系统性能,降低资源消耗,提高开发效率,使系统更加健壮和可扩展。

以上就是SpringBoot整合Redis实现序列化的7种策略详解的详细内容,更多关于SpringBoot Redis序列化的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文