原文作者:彌諾R
原文地址:http://www.minuor.com/1523882147/article
轉載聲明:轉載請註明原文地址,注意版權維護,謝謝!
寫前說明
在spring+spring mvc+mybatis模式下,使用的最多的就是jedis,但是spring boot整合了redis後,依然可以使用jedis,但是同時也提供了一個RedisTemplate和StringRedisTemplate,RedisTemplate使用的序列化類是默認JdkSerializationRedisSerializer,而StringRedisTemplate使用的序列化類默認是StringRedisSerializer,因此在存儲的方式上也是有所不同。簡單的說就是RedisTemplate的key和value可以是任意類型的數據,但是StringRedisTemplate的key和value只能是String,如果存儲其他類型,序列化和反序列化會存在問題。綜合來說,如果使用spring提供的redis連接就使用RedisTemplate,兼容性更高,如果使用jedis就無所謂了,因爲它默認就是支持各種數據類型的鍵值。
另外Redis羣的搭建可以參考[Redis集羣的簡單搭建和實現(待提供)]。
準備工作
需要在新建立一個模塊utui-common,這個模塊用於存放Redis封裝的工具類,在utui-dao中加入對utui-common的依賴座標。
依賴的加入
<dependency>
<groupId>com.springboot.utui</groupId>
<artifactId>utui-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
模塊結構
|-utui-common
|-src
|-main
|-java
|-com.springboot.utui.common
|-utils
|-RedisClientUtil
springboot整合集成RedisTemplate方式實現
######父pom.xml
添加spring-boot-starter-redis的依賴jar
<!-- spring整合redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
application.properties
#-------------------------------- redis properties start --------------------------------#
# redis數據庫索引(默認是0)
spring.redis.datasource=0
# redis服務器地址
spring.redis.host=127.0.0.1
# redis服務端口
spring.redis.port=6379
# redis服務器密碼(默認是空)
spring.redis.password=
# 連接池最大連接數(-1表示不限制)
spring.redis.pool.max-active=10
# 連接池最小空閒連接數
spring.redis.pool.max-idle=10
# 連接池最大空閒連接數
spring.redis.pool.min-idle=0
# 最大阻塞等待時間(-1表示不限制,單位:毫秒)
spring.redis.max-wait=3000
# 連接超時時間(單位:毫秒)
spring.redis.timeout=5000
# 集羣節點(三主三從)
# spring.redis.cluster.nodes=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
#-------------------------------- redis properties end --------------------------------#
RedisClientUtil.java
utui-common下添加RedisClientUtil類,用於封裝Redis的一些公用方法,爲了寫這個封裝花了挺長時間,並都測試了一遍,基本沒有問題,如果項目中有使用,需要封裝Redis工具類,可以直接拿去,不謝。
@Component
@Slf4j
public class RedisClientUtil {
@Autowired
RedisTemplate<String, String> redisTemplate;
/**
* 鍵值對設值
*
* @param key 鍵
* @param value 值
* @return
*/
public <K, V> Boolean set(K key, V value) {
return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
connection.set(JSON.toJSONBytes(key), JSON.toJSONBytes(value));
return true;
});
}
/**
* 鍵值對設值和有效時間
*
* @param key 鍵
* @param value 值
* @param time 有效時間(單位:秒)
* @return
*/
public <K, V> Boolean setEx(K key, V value, long time) {
return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
connection.setEx(JSON.toJSONBytes(key), time, JSON.toJSONBytes(value));
return true;
});
}
/**
* 查詢鍵值對
*
* @param key 鍵
* @param typeReference 返回類型
* @param <K> 鍵類型
* @param <R> 返回類型
* @return
*/
public <K, R> R get(K key, TypeReference<R> typeReference) {
byte[] redisValue = redisTemplate.execute((RedisCallback<byte[]>) connection -> connection.get(JSON.toJSONBytes(key)));
if (redisValue == null) return null;
return JSONObject.parseObject(new String(redisValue), typeReference);
}
/**
* 存儲Hash結構數據(批量)
*
* @param outerKey 外鍵
* @param map 內鍵-內值
* @return
*/
public <O, I, V> Boolean hSetMap(O outerKey, Map<I, V> map) {
if (map == null || map.isEmpty()) return false;
Map<byte[], byte[]> byteMap = new HashMap<>();
map.forEach((innerKey, innerValue) -> byteMap.put(JSON.toJSONBytes(innerKey), JSON.toJSONBytes(innerValue)));
return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
connection.hMSet(JSON.toJSONBytes(outerKey), byteMap);
return true;
});
}
/**
* 存儲Hash結構數據
*
* @param outerKey 外鍵
* @param innerKey 內鍵
* @param innerValue 值
* @return
*/
public <O, I, V> Boolean hSet(O outerKey, I innerKey, V innerValue) {
Map<I, V> map = new HashMap<>();
map.put(innerKey, innerValue);
return this.hSetMap(outerKey, map);
}
/**
* 獲取Hash結構Map集合,內鍵和內值鍵值對封裝成Map集合
*
* @param outerKey 外鍵
* @param innerKeyType 內鍵類型
* @param innerValueType 值類型
* @return
*/
public <O, I, V> Map<I, V> hGetMap(O outerKey, TypeReference<I> innerKeyType, TypeReference<V> innerValueType) {
Map<byte[], byte[]> redisMap = redisTemplate.execute
((RedisCallback<Map<byte[], byte[]>>) connection -> connection.hGetAll(JSON.toJSONBytes(outerKey)));
if (redisMap == null) return null;
Map<I, V> resultMap = new HashMap<>();
redisMap.forEach((key, value) -> resultMap.put(JSONObject.parseObject
(new String(key), innerKeyType), JSONObject.parseObject(new String(value), innerValueType)));
return resultMap;
}
/**
* 查詢Hash結構的值
*
* @param outerKey 外鍵
* @param innerKey 內鍵
* @param typeReference 值類型
* @return
*/
public <O, I, V> V hGet(O outerKey, I innerKey, TypeReference<V> typeReference) {
byte[] redisResult = redisTemplate.execute((RedisCallback<byte[]>)
connection -> connection.hGet(JSON.toJSONBytes(outerKey), JSON.toJSONBytes(innerKey)));
if (redisResult == null) return null;
return JSONObject.parseObject(new String(redisResult), typeReference);
}
/**
* 刪除鍵值對
*
* @param keys 鍵
* @return
*/
public <K> Long del(List<K> keys) {
if (keys == null || keys.isEmpty()) return 0L;
byte[][] keyBytes = new byte[keys.size()][];
int index = 0;
for (K key : keys) {
keyBytes[index] = JSON.toJSONBytes(key);
index++;
}
return redisTemplate.execute((RedisCallback<Long>) connection -> connection.del(keyBytes));
}
/**
* 刪除Hash結構內鍵和值
*
* @param outerKey 外鍵
* @param innerKeys 內鍵
* @return
*/
public <O, I> Long hDel(O outerKey, List<I> innerKeys) {
if (innerKeys == null || innerKeys.isEmpty()) return 0L;
byte[][] innerKeyBytes = new byte[innerKeys.size()][];
int index = 0;
for (I key : innerKeys) {
innerKeyBytes[index] = JSON.toJSONBytes(key);
index++;
}
return redisTemplate.execute((RedisCallback<Long>) connection ->
connection.hDel(JSON.toJSONBytes(outerKey), innerKeyBytes));
}
}
這裏很方便,因爲在spring和redis整合後,啓動項目時,spring會自動從application.properties中讀取redis的信息,並根據這些信息構建RedisTemplate和StringRedisTemplate對象,交由Spring管理。因此這裏可以直接注入。
RedisClientController.java
在com.springboot.utui.web.controller包下創建一個RedisClientController類,用於測試整合結果。
@Controller
@RequestMapping("/redis")
@Slf4j
public class RedisClientController {
@Autowired
private RedisClientUtil redisClient;
/**
* 測試鍵值對
*/
@RequestMapping(method = RequestMethod.GET, value = "/testKv")
@ResponseBody
public String testKv() {
Boolean insertResult = redisClient.set("test-key-1", "test-value-1");
redisClient.set("test-key-2", "test-value-2");
redisClient.set("test-key-3", "test-value-3");
String getResult = redisClient.get("test-key-1", new TypeReference<String>() {
});
return "insertResult:" + insertResult + ",getResult:" + getResult;
}
/**
* 測試Hash結構
*/
@RequestMapping(method = RequestMethod.GET, value = "/testKm")
@ResponseBody
public String testKm() {
Boolean hSetResult = redisClient.hSet("test-hSet-outer", "test-hSet-innerKey", "test-hSet-innerValue");
Map<String, String> innerMap = new HashMap<>();
innerMap.put("test-hSetMap-innerKey-1", "test-hSetMap-innerValue-1");
innerMap.put("test-hSetMap-innerKey-2", "test-hSetMap-innerValue-2");
innerMap.put("test-hSetMap-innerKey-3", "test-hSetMap-innerValue-3");
Boolean hSetMapResult = redisClient.hSetMap("test-hSetMap-outer", innerMap);
String hGetResult = redisClient.hGet("test-hSet-outer", "test-hSet-innerKey", new TypeReference<String>() {
});
Map<String, String> hGetMapResult = redisClient.hGetMap("test-hSetMap-outer", new TypeReference<String>() {
}, new TypeReference<String>() {
});
return "hSetResult:" + hSetResult + ",hGetResult:" + hGetResult + ",hSetMapResult:" + hSetMapResult + ",hGetMapResult:" + hGetMapResult;
}
/**
* 測試刪除
*/
@RequestMapping(method = RequestMethod.GET, value = "/testDel")
@ResponseBody
public String testDel() {
List<String> delList = new ArrayList<>();
delList.add("test-key-1");
delList.add("test-key-2");
Long delNum = redisClient.del(delList);
String delAfter = redisClient.get("test-key-1", new TypeReference<String>() {
});
List<String> hDelList = new ArrayList<>();
hDelList.add("test-hSetMap-innerKey-1");
hDelList.add("test-hSetMap-innerKey-2");
Long hDelNum = redisClient.hDel("test-hSet-outer", hDelList);
String hDelAfter = redisClient.hGet("test-hSet-outer", "test-hSetMap-innerKey-1", new TypeReference<String>() {
});
return "delNum:" + delNum + ",delAfter:" + delAfter + ",hDelNum:" + hDelNum + ",hDelAfter:" + hDelAfter;
}
}
功能驗證
步驟:
- 啓動項目
- 瀏覽器訪問:
http://localhost:8080/redis/testKv
,響應結果:insertResult:true,getResult:test-value-1
- 瀏覽器訪問:
http://localhost:8080/redis/testKm
,響應結果:hSetResult:true,hGetResult:test-hSet-innerValue,hSetMapResult:true,hGetMapResult:{test-hSetMap-innerKey-3=test-hSetMap-innerValue-3, test-hSetMap-innerKey-2=test-hSetMap-innerValue-2, test-hSetMap-innerKey-1=test-hSetMap-innerValue-1}
- 瀏覽器訪問:
http://localhost:8080/redis/testDel
,響應結果:delNum:2,delAfter:null,hDelNum:0,hDelAfter:null
####單機和集羣
上面的操作步驟都是基於一臺redis服務器來做的,如果是基於多臺redis服務,也就是集羣,這裏只要將spring.redis.host/spring.redis.port註釋掉,將spring.redis.cluster.nodes註釋放開就可以了,沒有其他什麼操作,就是這麼簡單,其他代碼都不需要動,上手測試即可。
springboot整合集成jedis方式實現(單機模式)
JedisPoolBootConfig.java
在com.springboot.utui.web下建立config包,創建JedisPoolBootConfig類。
@Configuration
public class JedisPoolBootConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Value("${spring.redis.max-wait}")
private Integer maxWait;
@Value("${spring.redis.pool.max-active}")
private Integer maxActive;
@Value("${spring.redis.pool.max-idle}")
private Integer maxIdle;
@Value("${spring.redis.pool.min-idle}")
private Integer minIdle;
@Value("${spring.redis.timeout}")
private int timeout;
@Bean
public ShardedJedisPool getShardedJedisPool() {
//Jedis配置信息
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWaitMillis(maxWait);
JedisShardInfo shardInfo = new JedisShardInfo(redisHost, redisPort, timeout);
List<JedisShardInfo> shardInfos = new ArrayList<>();
shardInfos.add(shardInfo);
return new ShardedJedisPool(jedisPoolConfig, shardInfos);
}
@Bean
public JedisPool getJedisPool() {
//Jedis配置信息
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxWaitMillis(maxWait);
return new JedisPool(jedisPoolConfig, redisHost, redisPort, timeout);
}
}
RedisClientController.java
在RedisClientController.java中導入ShardedJedisPool\JedisPool對象並添加測試方法。
@Autowired
private ShardedJedisPool shardedJedisPool;
@Autowired
private JedisPool jedisPool;
/**
* 測試JedisShardedJedis
*/
@RequestMapping(method = RequestMethod.GET, value = "/testShardedJedis")
@ResponseBody
public String testShardedJedis() {
ShardedJedis sharJedis = shardedJedisPool.getResource();
sharJedis.set("sharJedis-test", "sharJedis-test-value");
return "sharJedis:" + sharJedis.get("sharJedis-test-value");
}
/**
* 測試JedisPool
*/
@RequestMapping(method = RequestMethod.GET, value = "/testJedisPool")
@ResponseBody
public String testJedisPool() {
Jedis jedis = jedisPool.getResource();
jedis.set("jedis-test", "jedis-test-value");
return "jedis:" + jedis.get("jedis-test");
}
運行項目測試後得到相應的結果,這裏ShardedJedisPool和JedisPool是兩種實現單機Redis服務的方式,二者去其一即可,這裏作爲演示就都寫出來。
####springboot整合集成jedis方式實現(集羣模式)
JedisClusterConfig.java
在com.springboot.utui.web.config包中創建JedisClusterConfig類。
@Configuration
public class JedisClusterConfig {
@Value("${spring.redis.cluster.nodes}")
private String redisNodes;
@Value("${spring.redis.max-wait}")
private Integer maxWait;
@Value("${spring.redis.pool.max-idle}")
private Integer maxIdle;
@Value("${spring.redis.pool.min-idle}")
private Integer minIdle;
@Value("${spring.redis.timeout}")
private int timeout;
@Bean
public JedisCluster getJedisCluster() {
String[] cNodes = redisNodes.split(",");
Set<HostAndPort> nodes = new HashSet<>();
for (String cNode : cNodes) {
String[] hp = cNode.split(":");
nodes.add(new HostAndPort(hp[0], Integer.valueOf(hp[1])));
}
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxWaitMillis(maxWait);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
return new JedisCluster(nodes, timeout, poolConfig);
}
}
RedisClientController.java
在RedisClientController.java中導入JedisCluster對象並添加測試方法。
@Autowired
private JedisCluster jedisCluster;
/**
* 測試JedisCluster
*/
@RequestMapping(method = RequestMethod.GET, value = "/testJedisCluster")
@ResponseBody
public String testJedisCluster() {
jedisCluster.set("jedisCluster-test", "jedisCluster-test-value");
return "jedisCluster:" + jedisCluster.get("jedisCluster-test");
}
運行項目測試瀏覽器訪問得到對應結果。