When our data is stored in Redis, our keys and values (value) are serialized to the database through the serializer provided by spring. Redistemplate By default, Jdkserializationredisserializer,stringredistemplate is used by default, Stringredisserializer.
Spring Data JPA provides us with the following Serializer:generictostringserializer, Jackson2jsonredisserializer, Jacksonjsonredisserializer, Jdkserializationredisserializer, Oxmserializer, Stringredisserializer.
Comparison of serialization methods:
- Jdkserializationredisserializer: Use the serialization functionality provided by the JDK. The advantage is that you do not need to provide the type information (class) when deserializing, but the disadvantage is that the serializable interface needs to be implemented, and the result of serialization is very large, about 5 times times the JSON format, which consumes a lot of memory from the Redis server.
- Jackson2jsonredisserializer: Serializes an object into a JSON string using the Jackson Library. The advantage is that the speed is fast, the serialized string is short, and there is no need to implement the serializable interface. But the disadvantage is also very deadly, that is, there is a type parameter in the constructor for this class, and you must supply the type information (. class object) of the object you want to serialize. By looking at the source code, it is discovered that only type information is used during deserialization.
Problem description
When we were using spring data Redis as a Redis cache, we specified the problem with the Redistemplate key and value serialization.
- The Redistemplate key is specified as Stringredisserializer serialization to report type conversion errors, such as the XXX class cannot be converted to string.
- When serializing with Jackson2jsonredisserializer, an error occurs if no set method is deserialized on the entity class.
Problem analysis
Issue 1: When using Stringredisserializer to serialize a key, the generic type of Stringredisserializer specifies string, and other objects will report type conversion errors. When using the @cacheable annotation, the key attribute can only be passed in as a string. Rewrite this serialization method to change the generic to object. Source:
/*** The serializer must be rewritten, otherwise the key of the @cacheable annotation will report the type conversion error **/ Public classStringredisserializerImplementsRedisserializer<object> { Private FinalCharset Charset; Private FinalString target = "\" "; Private FinalString replacement = ""; PublicStringredisserializer () { This(Charset.forname ("UTF8")); } PublicStringredisserializer (Charset Charset) {assert.notnull (Charset,"Charset must not being null!"); This. CharSet =CharSet; } @Override PublicString Deserialize (byte[] bytes) { return(bytes = =NULL?NULL:NewString (Bytes, charset)); } @Override Public byte[] Serialize (Object object) {string string=json.tojsonstring (object); if(String = =NULL) { return NULL; } string=String.Replace (target, replacement); returnstring.getbytes (CharSet); }}
Question 2: We give up the serialization of value with Jackjson and use Fastjson to do it. Rewrite some serializers and implement the Redisserializer interface. The source code is as follows:
Public classFastjsonredisserializer<t>ImplementsRedisserializer<t> { Public Static FinalCharset default_charset = Charset.forname ("UTF-8"); PrivateClass<t>Clazz; PublicFastjsonredisserializer (class<t>clazz) { Super(); This. Clazz =Clazz; } @Override Public byte[] Serialize (T t)throwsSerializationException {if(T = =NULL) { return New byte[0]; } returnjson.tojsonstring (t, serializerfeature.writeclassname). GetBytes (Default_charset); } @Override PublicT Deserialize (byte[] bytes)throwsSerializationException {if(bytes = =NULL|| Bytes.length <= 0) { return NULL; } String str=NewString (bytes, default_charset); return(T) json.parseobject (str, clazz); }}
Add a new serialization Kryoredisserializer. Fast, the source code is as follows:
ImportCom.esotericsoftware.kryo.Kryo;ImportCom.esotericsoftware.kryo.io.Input;ImportCom.esotericsoftware.kryo.io.Output;ImportOrg.slf4j.Logger;Importorg.slf4j.LoggerFactory;ImportOrg.springframework.data.redis.serializer.RedisSerializer;Importorg.springframework.data.redis.serializer.SerializationException;ImportJava.io.ByteArrayOutputStream;/** * @param<T> *@authorYuhao.wang*/ Public classKryoredisserializer<t>ImplementsRedisserializer<t>{Logger Logger= Loggerfactory.getlogger (Kryoredisserializer.class); Public Static Final byte[] Empty_byte_array =New byte[0]; Private Static Finalthreadlocal<kryo> Kryos = threadlocal.withinitial (kryo::New); PrivateClass<t>Clazz; PublicKryoredisserializer (class<t>clazz) { Super(); This. Clazz =Clazz; } @Override Public byte[] Serialize (T t)throwsSerializationException {if(T = =NULL) { returnEmpty_byte_array; } Kryo Kryo=Kryos.get (); Kryo.setreferences (false); Kryo.register (Clazz); Try(Bytearrayoutputstream BAOs =NewBytearrayoutputstream (); Output Output=NewOutput (BAOs)) {kryo.writeclassandobject (output, t); Output.flush (); returnBaos.tobytearray (); } Catch(Exception e) {logger.error (E.getmessage (), E); } returnEmpty_byte_array; } @Override PublicT Deserialize (byte[] bytes)throwsSerializationException {if(bytes = =NULL|| Bytes.length <= 0) { return NULL; } Kryo Kryo=Kryos.get (); Kryo.setreferences (false); Kryo.register (Clazz); Try(Input input =NewInput (bytes)) { return(T) kryo.readclassandobject (input); } Catch(Exception e) {logger.error (E.getmessage (), E); } return NULL; }}
Use of custom serialization
@Configuration Public classRedisconfig {/*** Rewrite the Redis serialization method using JSON: * When our data is stored in Redis, our key (key) and values (value) are serialized to the database through the serializer provided by spring. Redistemplate By default, Jdkserializationredisserializer,stringredistemplate is used by default, Stringredisserializer. * Spring Data JPA provides us with the following serializer: * Generictostringserializer, Jackson2jsonredisserializer, Jacksonjsonredisserializer, Jdkserializationredisserializer, Oxmserializer, Stringredisserializer. * Here we will configure ourselves redistemplate and define serializer. * * @paramRedisconnectionfactory *@return */@Bean PublicRedistemplate<string, object>redistemplate (redisconnectionfactory redisconnectionfactory) {redistemplate<string, object> redistemplate =NewRedistemplate<>(); Redistemplate.setconnectionfactory (redisconnectionfactory); Fastjsonredisserializer<Object> Fastjsonredisserializer =NewFastjsonredisserializer<> (Object.class); //Global Open Autotype, not recommended//parserconfig.getglobalinstance (). Setautotypesupport (True); //It is recommended to use this method to specify a whitelist for small scopesParserconfig.getglobalinstance (). Addaccept ("Com.xiaolyuh."); //the serialization of the Set value (value) takes Fastjsonredisserializer. Redistemplate.setvalueserializer (Fastjsonredisserializer); Redistemplate.sethashvalueserializer (Fastjsonredisserializer); //the serialization of the Set key (key) takes Stringredisserializer. Redistemplate.setkeyserializer (NewStringredisserializer ()); Redistemplate.sethashkeyserializer (NewStringredisserializer ()); Redistemplate.afterpropertiesset (); returnredistemplate; }}
Redis serialization Methods Stringredisserializer, Fastjsonredisserializer, and Kryoredisserializer