1 簡介
之前講過如何通過Docker
安裝Redis
,也講了Springboot
以Repository
方式整合Redis
,建議閱讀后再看本文效果更佳:
(1) Docker安裝Redis並介紹漂亮的可視化客戶端進行操作
(2) 實例講解Springboot以Repository方式整合Redis
本文將通過實例講解Springboot
以Template
方式整合Redis
,並遇到一些序列化的問題。代碼結構如下:
2 整合過程
與文章《實例講解Springboot以Repository方式整合Redis》相同的代碼不再列出來,文末將提供代碼下載方式。
2.1 自動配置類
把相關依賴引入到項目中后,Springboot
就自動幫我們生成了Template
類,分別是RedisTemplate
和StringRedisTemplate
。看一下自動配置類能看出這兩個類都已經創建到Spring容器里了。
public class RedisAutoConfiguration { public RedisAutoConfiguration() { } @Bean @ConditionalOnMissingBean( name = {"redisTemplate"} ) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
實際上StringRedisTemplate
是RedisTemplate
的子類,對於String
類型,更推薦使用前者,它的類型只能是String
的,會有類型檢查上的安全;而RedisTemplate
可以操作任何類型。
2.2 實現數據訪問層
本文通過RedisTemplate
對Redis
進行操作,所以我們需要將它注入進來。代碼如下:
package com.pkslow.springbootredistemplate.dal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Repository; @Repository public class UserDAL { @Autowired private RedisTemplate<Object, Object> redisTemplate; public void setValue(Object key, Object value) { redisTemplate.opsForValue().set(key, value); } public Object getValue(Object key) { return redisTemplate.opsForValue().get(key); } }
RedisTemplate
提供了豐富的方法,具體可以參考官方文檔,本次用到的及類似的方法有:
- opsForHash(): 返回對於Hash的操作類;
- opsForList(): 返回對於列表List的操作類;
- opsForSet(): 返回對於Set的操作類;
- opsForValue(): 返回對於字符串String的操作類;
- opsForZSet(): 返回對於ZSet的操作類。
2.3 實現Controller
我們需要把功能通過Web
的方式暴露出去,實現以下Contrller
:
package com.pkslow.springbootredistemplate.controller; import com.pkslow.springbootredistemplate.dal.UserDAL; import com.pkslow.springbootredistemplate.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/userTemplate") public class UserTemplateController { @Autowired private final UserDAL userDAL; public UserTemplateController(UserDAL userDAL) { this.userDAL = userDAL; } @GetMapping("/{userId}") public User getByUserId(@PathVariable String userId) { return (User)userDAL.getValue(userId); } @PostMapping("/{userId}") public User addNewUser(@PathVariable String userId, @RequestBody User user) { user.setUserId(userId); userDAL.setValue(userId, user); return user; } }
只提供兩個接口,分別是設值和取值。
2.4 通過Postman測試
(1)存入對象
(2)讀取對象
能寫能讀,功能實現,完美!Perfect!收工!
3 序列化問題
程序功能正常運行一段時間后,運維殺來了:“這是什么東西?我怎么看得懂?我要怎么查看數據?”
3.1 定位問題
不得不重新打開項目代碼,Debug
一下看看哪出了問題。既然用Postman
測試能正常顯示,而數據庫顯示不對,說明是寫入數據庫時做了轉換。查看RedisTemplate
就行了,畢竟活是他干的(先瘋狂甩鍋)。
看它的序列化類用的是默認的JdkSerializationRedisSerializer
,所以序列化后的數據我們看不懂。
3.2 問題修復
甩鍋完后,還是要修復問題的,畢竟代碼是自己寫的。關鍵就是替換掉RedisTemplate
所使用的序列化類就行了,這有兩個方案可選:
(1)自定義一個新的RedisTemplate
以覆蓋舊的,在定義的時候指定序列化類。大致代碼如下:
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(jackson2JsonRedisSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashKeySerializer(jackson2JsonRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; }
甚至還可以自定義RedisConnectionFactory
,如下:
@Bean JedisConnectionFactory jedisConnectionFactory() { JedisConnectionFactory jedisConFactory = new JedisConnectionFactory(); jedisConFactory.setHostName("localhost"); jedisConFactory.setPort(6379); return jedisConFactory; }
(2)使用原有的RedisTemplate
,在使用前替換掉序列化類
引用的類的代碼如下,init
方法作為初始化方法:
public class UserDAL { @Autowired private RedisTemplate<Object, Object> redisTemplate; public void init() { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); } public void setValue(Object key, Object value) { redisTemplate.opsForValue().set(key, value); } public Object getValue(Object key) { return redisTemplate.opsForValue().get(key); } }
然后在創建UserDAL
時,代碼如下:
@Bean(initMethod = "init") public UserDAL userDAL() { return new UserDAL(); }
重新提交代碼、重新測試、重新發布,結果可以了:
4 總結
本文詳細代碼可在南瓜慢說公眾號回復<SpringbootRedisTemplate>獲取。