1. 程式人生 > >例項講解Springboot以Template方式整合Redis及序列化問題

例項講解Springboot以Template方式整合Redis及序列化問題

1 簡介

之前講過如何通過Docker安裝Redis,也講了SpringbootRepository方式整合Redis,建議閱讀後再看本文效果更佳:

(1) Docker安裝Redis並介紹漂亮的視覺化客戶端進行操作

(2) 例項講解Springboot以Repository方式整合Redis

本文將通過例項講解SpringbootTemplate方式整合Redis,並遇到一些序列化的問題。程式碼結構如下:

2 整合過程

與文章《例項講解Springboot以Repository方式整合Redis》相同的程式碼不再列出來,文末將提供程式碼下載方式。

2.1 自動配置類

把相關依賴引入到專案中後,Springboot

就自動幫我們生成了Template類,分別是RedisTemplateStringRedisTemplate。看一下自動配置類能看出這兩個類都已經建立到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;
    }
}

實際上StringRedisTemplateRedisTemplate的子類,對於String型別,更推薦使用前者,它的型別只能是String的,會有型別檢查上的安全;而RedisTemplate可以操作任何型別。

2.2 實現資料訪問層

本文通過RedisTemplateRedis進行操作,所以我們需要將它注入進來。程式碼如下:

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>獲取。


歡迎訪問南瓜慢說 www.pkslow.com獲取更多精彩文章!

歡迎關注微信公眾號<南瓜慢說>,將持續為你更新...

多讀書,多分享;多寫作,多整理