原文作者:程序猿杨鲍
转载声明:转载请注明原文地址,注意版权维护,谢谢!
写前说明
在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;
}
}
功能验证
步骤:
1. 启动项目
2. 浏览器访问:http://localhost:8080/redis/testKv
,响应结果:insertResult:true,getResult:test-value-1
3. 浏览器访问: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}
4. 浏览器访问: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");
}
运行项目测试浏览器访问得到对应结果。