skip to content

How do you configure serializers on a RedisTemplate, and why is the choice important?

level: middleimportance: must knowfreq 60%

answer

  1. four serializers: key, value, hashKey, hashValue
  2. String keys + JSON values is the standard combo
  3. GenericJackson embeds @class type hint; Jackson2Json<T> is typed
  4. don't forget the hash serializers
  5. JDK serializer = binary, brittle, RCE risk

basics

~10 s

Define a RedisTemplate bean and call setKeySerializer/setValueSerializer (and the hash variants). Common choices: StringRedisSerializer for keys and a JSON serializer for values, so data is readable and language-neutral.

solid answer

~40 s

RedisTemplate has four independent serializers: key, value, hashKey, hashValue. If you don't set them, plain RedisTemplate uses JdkSerializationRedisSerializer everywhere — opaque, Java-coupled, and unreadable. Best practice: build a custom RedisTemplate<String,Object> bean, set StringRedisSerializer for keySerializer and hashKeySerializer (readable keys, correct HGETALL field names), and GenericJackson2JsonRedisSerializer (or Jackson2JsonRedisSerializer<T>) for value/hashValue so values are JSON and interoperable. GenericJackson2JsonRedisSerializer embeds an @class type hint so it can deserialize back to the concrete type; Jackson2JsonRedisSerializer<T> is typed to one class and omits the hint. Call afterPropertiesSet() (or rely on the container). Serializer choice affects readability in redis-cli, cross-language interop, payload size, and whether stored data survives class refactors — JDK serialization breaks on serialVersionUID/class changes.

code

java · 19 lines
java
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(cf);

        StringRedisSerializer keys = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer json = new GenericJackson2JsonRedisSerializer();

        template.setKeySerializer(keys);
        template.setHashKeySerializer(keys);
        template.setValueSerializer(json);      // JSON, readable + interoperable
        template.setHashValueSerializer(json);  // don't leave hashes on JDK default!
        template.afterPropertiesSet();
        return template;
    }
}

go deeper

for a junior

Should know serializers convert objects to bytes and that String+JSON is common.

for a middle

Should configure all four serializers correctly and explain readability/interop tradeoffs.

for a senior

Should discuss @class type hints, refactor/versioning safety, and the JDK-deserialization security risk.

for a principal

Should standardize a serializer policy across services, weigh payload size vs interop, and separate template config from repository conversions.

**Why serializers exist** — Redis stores only byte arrays. Every key and value your app sends must be converted `Object -> byte[]` on write and `byte[] -> Object` on read. That conversion is a **RedisSerializer<T>**. RedisTemplate holds *four* of them because Redis hashes have their own field keys and values: - **keySerializer** — for the top-level key (e.g. `user:1`). - **valueSerializer** — for simple values stored via opsForValue. - **hashKeySerializer** — for the *field names* inside a Redis hash (opsForHash). - **hashValueSerializer** — for the *field values* inside a hash. There's also a `defaultSerializer` used to fill any you don't set. **The built-in serializers:** - **JdkSerializationRedisSerializer** — the default for plain RedisTemplate. Uses Java's `ObjectOutputStream`. Downsides: binary/unreadable, requires `Serializable`, tightly coupled to the exact class + `serialVersionUID`, not readable by non-Java clients, and brittle across refactors. Only virtue: zero config for Java-only, throwaway data. - **StringRedisSerializer** — UTF-8 text. Use for keys and hash keys almost always, so `redis-cli KEYS *` and `HGETALL` are legible and so keys match what other services expect. - **GenericJackson2JsonRedisSerializer** — serializes any object to JSON and embeds an `@class` field carrying the fully-qualified type name, letting it deserialize back to the right concrete type without you specifying it. Great for `RedisTemplate<String,Object>` holding heterogeneous types. The `@class` hint adds bytes and couples the JSON to your package names. - **Jackson2JsonRedisSerializer<T>** — typed to one class T; produces clean JSON with no `@class` hint, but you must know the target type at read time. Good for a single-type template. - **StringRedisSerializer + your own format**, **OxmSerializer** (XML), etc. also exist. **How to configure** — define a `@Bean RedisTemplate<String,Object>`: ```java template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.afterPropertiesSet(); ``` `afterPropertiesSet()` validates and applies the default serializer to any slot you left null. (When the bean is created by Spring's container as a normal `@Bean`, `InitializingBean.afterPropertiesSet()` is invoked for you; calling it manually is safe and explicit.) **Common bug: forgetting hash serializers** — people set key + value serializers but leave hashKey/hashValue as the JDK default, then find hash fields are binary. Always set all four (or the relevant ones). **Why the choice matters:** 1. **Readability/observability** — StringRedisSerializer + JSON means you can debug with redis-cli. 2. **Interoperability** — JSON is language-neutral; a Python or Node service can read it. JDK serialization locks data to Java. 3. **Payload size** — JSON is larger than a compact binary format but readable; the `@class` hint adds overhead. 4. **Refactor safety** — JDK serialization breaks when the class or serialVersionUID changes; JSON tolerates additive changes with lenient Jackson config. 5. **Security** — deserializing untrusted data with JDK serialization is a known RCE risk; JSON with a controlled type is safer, though `@class`/polymorphic type handling still needs care. **Interaction with @RedisHash repositories** — those use a *separate* mechanism (RedisMappingContext + conversions), not the template's serializers. Configuring the template does not change how repository entities are stored. **When to use what** — text-only data: StringRedisTemplate. Structured objects you want readable and cross-language: StringRedisSerializer keys + Generic/Jackson JSON values. Purely Java-internal ephemeral cache where you never inspect it: JDK default is acceptable but still discouraged.

  • What is the practical difference between GenericJackson2JsonRedisSerializer and Jackson2JsonRedisSerializer<T>?
    GenericJackson2JsonRedisSerializer embeds an @class type hint so it can deserialize heterogeneous values back to their concrete type; Jackson2JsonRedisSerializer<T> is bound to a single type T, produces cleaner JSON without the hint, but can only read that one type.
  • You set key and value serializers but hash fields still show binary. Why?
    You didn't set hashKeySerializer/hashValueSerializer, so they fell back to the JDK default. Set all four serializers.

saying these in an interview costs you the question

  • Setting only key+value serializers and forgetting the hash serializers
  • Believing configuring the template also changes how @RedisHash repositories serialize entities
  • Assuming JSON is the default and no configuration is needed
  • Not recognizing the security risk of JDK deserialization of untrusted data

context