《SpringBoot從入門到放棄》之第(十)篇——整合Redis(SpringBoot 2.0 版本),寫於2018年10月24號程式設計師節。
在 pom.xml 配置中新增 jar 依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
在 application.properties 配置檔案裡新增 Redis 配置:
# Redis 配置 # Redis 的IP地址,根據實際情況配置 spring.redis.host=192.168.126.130 # Redis 的埠號,預設 6379,如果沒有特別要求,可以不配置。 spring.redis.port=6379 # 連線 Redis 的密碼 spring.redis.password=123456 # 使用的庫,預設 0 spring.redis.database=0 # 客戶端連線超時時間,單位:毫秒,預設2000 spring.redis.timeout=10000 # 連線池中最大空閒數,預設是 8 ,如果沒有特別要求,可以不配置。 spring.redis.jedis.pool.max-idle=8 # 連線池中最大連線數,預設是 8 ,如果沒有特別要求,可以不配置。 spring.redis.jedis.pool.max-active=8 # 連線池中的最小空閒連線,預設是 0 ,如果沒有特別要求,可以不配置。 spring.redis.jedis.pool.min-idle=0
其實,很多配置 Redis 都有預設的配置,我們可以追蹤看看。按住 Ctrl + 滑鼠左鍵,點中配置檔案裡的一些配置,就進入到 jar 包裡:
public class RedisProperties { private int database = 0; private String url; private String host = "localhost"; private String password; private int port = 6379; public static class Pool { private int maxIdle = 8; private int minIdle = 0; private int maxActive = 8; private Duration maxWait = Duration.ofMillis(-1L);
因此,如果一些配置跟預設的一致,就無需寫到 application.properties 配置檔案裡了。
在寫程式碼的時候發現,JedisConnectionFactory 從SpringBoot 2.0 版本以後,所有的 setXXX 方法都過時了,
查資料才知道:JedisConnectionFacotory 從 spring-boot-starter-data-redis 2.0開始已經不推薦直接顯示設定連線的資訊了,一方面為了使配置資訊與建立連線工廠解耦,另一方面抽象出Standalone,Sentinel和RedisCluster三種模式的環境配置類和一個統一的 jedis 客戶端連線配置類(用於配置連線池和SSL連線),使得可以更加靈活方便根據實際業務場景需要來配置連線資訊。
O的K,那就用 2.0 的特性來學習吧。
新建 util 包,用來存放一些工具類的。後面接觸分散式開發後,還會單獨把工具類抽離出來,弄成一個 jar 包,用來作為公司的公共的工具類。
這裡涉及 3 個新的 java 檔案,分別是:
RedisConfig類:
package com.test.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String host;//主機地址
@Value("${spring.redis.port}")
private Integer port;//埠號
@Value("${spring.redis.password}")
private String password;//連線密碼
@Value("${spring.redis.database}")
private Integer database;//預設連線的庫
@Value("${spring.redis.timeout}")
private Integer timeout;//超時時間
@Value("${spring.redis.jedis.pool.max-idle}")
private Integer maxIdle;//連線池中最大空閒數
@Value("${spring.redis.jedis.pool.max-active}")
private Integer maxActive;//連線池中最大連線數
@Value("${spring.redis.jedis.pool.min-idle}")
private Integer minIdle;//連線池中的最小空閒連線
@Bean(name = "MyRedisTemplateUtil")//Bean的名稱可以自定義
public MyRedisTemplateUtil myRedisTemplate(){
MyRedisTemplateUtil myRedisTemplate = new MyRedisTemplateUtil();
myRedisTemplate.setRedisTemplate(redisTemplate());
return myRedisTemplate;
}
/**
* 建立工廠連線類物件,主要為模板 RedisTemplate 的 setConnectionFactory 方法服務。
* @return
*/
@Bean
public RedisConnectionFactory redisConnectionFactory(){
//Redis的基本配置
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setDatabase(database);
configuration.setHostName(host);
configuration.setPort(port);
configuration.setPassword(RedisPassword.of(password));
//配置連線池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxTotal(maxActive);
//使用工廠類建立連線物件
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder builder = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();
builder.poolConfig(poolConfig);
JedisConnectionFactory factory = new JedisConnectionFactory(configuration,builder.build());
factory.afterPropertiesSet();//在所有的屬性被初始化後呼叫,但是會在init前呼叫
RedisConnectionFactory connectionFactory = factory;
return connectionFactory;
}
/**
* 資料操作模板
* @return
*/
@Bean
public RedisTemplate redisTemplate(){
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory());//setConnectionFactory方法要求物件屬於類:RedisConnectionFactory
//採用JdkSerializationRedisSerializer的二進位制資料序列化方式
Jackson2JsonRedisSerializer jackson2Serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2Serializer.setObjectMapper(mapper);
//方式①:把 key 序列化成String型別,比較符合的習慣
RedisSerializer<String> redisSerializer = new StringRedisSerializer();//StringRedisSerializer是RedisSerializer的實現類
template.setKeySerializer(redisSerializer);
template.setHashKeySerializer(redisSerializer);
//方式②:把 key 序列化成Json格式,key會變成類似:“myKey”的形式
//template.setKeySerializer(jackson2Serializer);
//template.setHashKeySerializer(jackson2Serializer);
template.setValueSerializer(jackson2Serializer);
template.setHashValueSerializer(jackson2Serializer);
template.afterPropertiesSet();
return template;
}
}
需要注意的是:
①配置類需要新增 @Configuration 註解:import org.springframework.context.annotation.Configuration;
②使用 @Value("${spring.redis.host}") 語法獲取已載入到 Spring 容器的配置資訊
③@Bean(name = "MyRedisTemplateUtil")//Bean的名稱可以自定義,把 Bean 交給 Spring 容器來管理。
④在資料操作模板的方法裡,有兩種序列化的方式,按照對應的場景來使用,有人喜歡統一的Json格式,有人喜歡String格式。區別如下圖:
MyRedisTemplateUtil 類,封裝了操作 Redis 的一部分方法,更多的方法可以試著自己封裝:
package com.test.util;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
@Repository
public class MyRedisTemplateUtil{
private RedisTemplate redisTemplate;
public RedisTemplate getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 判斷 key 是否存在
* @param key
* @return
*/
public boolean hasKey(String key){
return getRedisTemplate().hasKey(key);
}
/**
* 刪除key
* @param key
* @return
*/
public Boolean del(String key){
return getRedisTemplate().delete(key);
}
/**
* string型別:設定值
* @param key
* @param value
* @return
*/
public void set(String key, String value){
getRedisTemplate().opsForValue().set(key,value);
}
/**
* string型別:獲取值
* @param key
* @return
*/
public String get(String key){
return (String)getRedisTemplate().opsForValue().get(key);
}
/**
* list型別:獲取列表裡所有值,start=0,end=-1
* @param key
* @return
*/
public List<String> range(String key){
try{
return (List) getRedisTemplate().opsForList().range(key,0L,-1L);
}catch (Exception e){
e.printStackTrace();
return new ArrayList<>();
}
}
/**
* list型別:從右邊(尾部)新增一個元素
* @param key
* @param value
*/
public Boolean rightPush(String key,String value){
try{
getRedisTemplate().opsForList().rightPush(key,value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* list型別:從左邊(頭部)彈出一個元素
* @param key
* @return
*/
public String leftPop(String key){
try{
return (String)getRedisTemplate().opsForList().leftPop(key);
}catch (Exception e){
e.printStackTrace();
return "";
}
}
}
說明:Long型別的引數,請在數字後面加上大寫的 L,而不是小寫的 l,因為小寫的 l 和數字 1 很像,容易搞錯!
如果還有疑問,請查閱我的部落格:Redis五種資料型別string、list、hash、set、zset
RedisController 測試的類:
package com.test.web;
import com.test.util.MyRedisTemplateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class RedisController {
@Autowired
@Qualifier("MyRedisTemplateUtil")//@Qualifier是標註@Bean=myRedisTemplateUtilUtil的類才是我們需要的
private MyRedisTemplateUtil myRedisTemplateUtil;
/**
* 刪除值
* @param key
* @return
*/
@RequestMapping(value = "/del")
public Map<String,Boolean> del(@RequestParam String key){
boolean result = myRedisTemplateUtil.del(key);
Map<String, Boolean> map = new HashMap<>();
map.put("result", result);
return map;
}
/**
* 判斷值是否存在
* @param key
* @return
*/
@RequestMapping(value = "/hasKey")
public Map<String,Boolean> hasKey(@RequestParam String key){
Boolean result = myRedisTemplateUtil.hasKey(key);
Map<String, Boolean> map = new HashMap<>();
map.put("result", result);
return map;
}
/**
* string型別:設定值
* @param key
* @param value
* @return
*/
@RequestMapping(value = "/set")
public Map<String,String> set(@RequestParam String key, @RequestParam String value){
myRedisTemplateUtil.set(key,value);
Map<String, String> map = new HashMap<>();
map.put("result", "success");
return map;
}
/**
* string型別:獲取值
* @param key
* @return
*/
@RequestMapping(value = "/get")
public Map<String,String> get(@RequestParam String key){
String result = myRedisTemplateUtil.get(key);
Map<String, String> map = new HashMap<>();
map.put("result", result);
return map;
}
/**
* list型別:獲取列表裡所有的值
* @param key
* @return
*/
@RequestMapping(value = "/range")
public Map<String,List<String>> range(@RequestParam String key){
List<String> result = myRedisTemplateUtil.range(key);
Map<String, List<String>> map = new HashMap<>();
map.put("result", result);
return map;
}
/**
* list型別:從右邊(尾部)新增一個元素
* @param key
* @param value
* @return
*/
@RequestMapping(value = "rightPush")
public Map<String,Boolean> rightPush(@RequestParam String key,@RequestParam String value){
Boolean result = myRedisTemplateUtil.rightPush(key,value);
Map<String, Boolean> map = new HashMap<>();
map.put("result", result);
return map;
}
/**
* list型別:從左邊(頭部)彈出一個元素
* @param key
* @return
*/
@RequestMapping(value = "leftPop")
public Map<String,String> leftPop(@RequestParam String key){
String result = myRedisTemplateUtil.leftPop(key);
Map<String, String> map = new HashMap<>();
map.put("result", result);
return map;
}
}
O的K,用 postman 來測試,測試之前,請開啟 Linux 虛擬機器,開啟 Redis,確保能連通:
如有疑問,請閱讀我的相關部落格,Java連線Redis和Redis Desktop的使用
測試 string 型別:
①測試 set 方法設定值:http://localhost:9090/set?key=biandan&value=讓天下沒有難寫的程式碼
②測試 get 方法獲取值:http://localhost:9090/get?key=biandan
結果:{"result": "讓天下沒有難寫的程式碼"}
③測試 hasKey 方法,判斷某個 key 是否存在:http://localhost:9090/hasKey?key=biandan
結果:{"result":true}
④刪除 key:http://localhost:9090/del?key=biandan
結果:{"result":true}
刪除後,再次查詢:http://localhost:9090/get?key=biandan
結果:{"result":null}
測試 list 型別:
①測試 rightPush 方法,從尾部依次新增元素,注意 key 要相同,才能測出效果:
http://localhost:9090/rightPush?key=alibaba&value=天貓雙11
http://localhost:9090/rightPush?key=alibaba&value=馬雲說,人要有夢想。而他的夢想是:讓天下沒有難做的生意。
結果:{"result":true}
②測試 range 方法,查詢 alibaba 的所有元素:http://localhost:9090/range?key=alibaba
結果:{"result":["天貓雙11","馬雲說,人要有夢想。而他的夢想是:讓天下沒有難做的生意。"]}
③測試 leftPop 方法,從左邊(頭部)彈出一個元素:http://localhost:9090/leftPop?key=alibaba
結果:{"result":"天貓雙11"}
④再次測試 range 方法:http://localhost:9090/range?key=alibaba
結果:{"result":["馬雲說,人要有夢想。而他的夢想是:讓天下沒有難做的生意。"]}
說明第一個元素 “天貓雙11” 已經被移除了。
更多關於 RedisTemplate 的知識,可以查閱:如何使用RedisTemplate訪問Redis資料結構
紙上得來終覺淺,絕知此事要躬行。敲程式碼越多,思考越多,進步越快,距離架構師越近。