業務場景中經常遇到使用Redis作爲緩存,而將對象寫入Redis更是常見的。下面來看下,對象寫入Redis的倆種方式(我之前就知道除了JDK,還有其它的諸如Jackson提供序列化功能,但是JDK之外的其它第三方在方序列化的時候提供目標對象class,不過今天我發現Spring-data-redis自帶的這個GenericJackson2JsonRedisSerializer解決了我的煩惱)。
1.公用配置
List-1 連接池和ConnectionFactory配置
<bean id="pool" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="30"/>
<property name="maxIdle" value="10"/>
<property name="minEvictableIdleTimeMillis" value="100000"/>
<property name="numTestsPerEvictionRun" value="1024"/>
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!--<constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"/>-->
<constructor-arg name="poolConfig" ref="pool"/>
<property name="hostName" value="localhost"/>
<property name="port" value="6379"/>
<property name="database" value="7"/>
</bean>
2.用JDK序列化方式
List-2 使用JDK的序列化方式
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
</bean>
如List-2所示,我們要將keySerializer和valueSerializer的值設置爲JdkSerializationRedisSerializer。
3.用Jackson方式
List-2 使用Jackson作爲序列化
<bean id="jacksonRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
</bean>
我們都知道Jackson反序列化的時候,都需要傳入目標對象class,這裏我們並不需要指定目標class,那麼問題來了,它是怎麼做到在使用者不提供目標class的情況下,方序列化的呢?
先來定義實體類,如下List-3和List-4
List-3 Department.java
/**
* @author [email protected] 2018-09-18 01:21
* @version 1.0
* @since 1.0
*/
@Data
public class Department {
private String name;
private List<OfficeStaff> officeStaffs;
private List<String> strings = Collections.emptyList();
private String other;
public Department(String name, List<OfficeStaff> officeStaffs, List<String> strings) {
this.name = name;
this.officeStaffs = officeStaffs;
this.strings = strings;
}
}
List-4 OfficeStaff.java
/**
* @author [email protected] 2018-09-18 01:22
* @version 1.0
* @since 1.0
*/
@Data
@AllArgsConstructor
public class OfficeStaff {
private String name;
private Integer age;
}
如下List-5所示,Jackson序列化方式將Department寫入Redis後,對象被轉換爲json字符串,同時,還有額外的"@class"字段表示對象類型。看到這個"@class",應該明白了,這個在方序列化的時候用到,由於在序列化寫入Redis的時候就將對象類型寫入到Redis了,所以方序列化的時候不需要提供目標對象class。
List-5 Department對象在Redis中存儲的是json
127.0.0.1:6379[7]> get "\"System\""
"{\"@class\":\"com.mjduan.project.objectRedisTemplate.model.Department\",\"name\":\"System\",\"officeStaffs\":[\"java.util.ArrayList\",[{\"@class\":\"com.mjduan.project.objectRedisTemplate.model.OfficeStaff\",\"name\":\"Jack\"}]],\"other\":\"other\"}"
127.0.0.1:6379[7]>
注:除了引入Spring-data-redis和Jedis,還需要引入jackson的依賴,我使用的版本如下List-6:
List-6
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency>
3.選型
在將對象序列化到Redis中時,該選擇JDK方式還是Jackson方式,我會選擇使用Jackson的方式,why,因爲JDK序列化方式很大的一個限制就是要求對象必須實現Serializable,而且是遞歸的(比如類A有個屬性類B,那麼類A和類B都要實現Serializable),特別是對遺留系統,很多存入緩存的對象很有可能沒有實現Serializable,如果使用JDK序列化方式的話,需要確認所有的對象都實現了Serializable,這個很耗時,且不一定能完全覆蓋。當然了,選擇Jackson序列化方式,也有未知的問題,只是目前還沒遇到。
思考:
- List-5中,Department並沒有名爲"@class"的屬性,那麼它是怎麼被添加進去的呢,這個問題暫時不研究了。