springboot整合redis作为K-V数据库
redis简介
springboot整合redis
新建工程并配置jedis客户端
首先使用spring-initializr
新建一个springboot
项目,选上依赖为web
。如下图所示:
然后打开pom
文件,添加jedis
,fastJson
,guava
的依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
然后开始配置一些参数,修改application.properties
文件:
#redis
redis.host=114.212.80.2
redis.port=6379
redis.timeout=3
redis.poolMaxTotal=10
redis.poolMaxIdle=10
redis.poolMaxWait=3
新建RedisConfig.groovy
类,用于加载redis
的配置参数。使用groovy
来编写这种bean
类可以很方便的省略掉set
和get
这种冗余代码。由于Jedis
是通过JedisPool
来获取,所以需要配置JedisPool
这个bean
,并通过spring
容器进行管理。
@Component
@ConfigurationProperties(prefix = "redis")
class RedisConfig {
//字段和application.properties中redis开头的配置属性一致
String host;
int port;
int timeout;//秒
String password;
int poolMaxTotal;
int poolMaxIdle;
int poolMaxWait;//秒
}
@Configuration
public class RedisPoolConfig {
@Autowired
RedisConfig redisConfig;
@Bean
public JedisPool configJedisPool() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
//需要注意的是时间的单位都是毫秒,我们的配置中都是秒,所以要*1000
poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000);
JedisPool jp = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0);
return jp;
}
}
这样,我们就把JedisPool
这个bean
托管给spring
容器了,使用的时候只需要@AutoWired
便能注入。
测试一下,编写RedisTest,注入JedisPool并得到Jedis实例,调用set和get方法测试是否能访问redis:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedisDemoApplication.class)
public class RedisTest {
@Autowired
JedisPool jedisPool;
@Test
public String get(String key){
key = "name";
System.out.println(jedisPool.getResource().get(key));//aaa
}
@Test
public boolean set(String key,String value){
key = "name";value = "aaa";
jedisPool.getResource().set(key, value);
return true;
}//仅做测试,未关闭jedis客户端等资源
结果如下
redis的通用方法封装及自定义Key生成器
默认来说,jedis仅支持String类型的set与get,所以我们的java对象想要保存在redis中必须通过序列化,这里采用json序列化,序列化工具采用fastJson。而且redis中的key必须是全局唯一的,所以我们在保存对象到redis中的时候需要采取一定的key生成策略,确保不会有旧的key被新key覆盖,在这里采用前缀+属性+属性值的方法,前缀一般为UserKey、BlogKey等一般根据模块来区分;属性一般根据前缀中包含的字段来命名,比如name、id等;属性值则为属性对应的值。
为了实现通用的Key生成器,我们首先定义一个KeyPrefix接口,然后让不同的Key实现它,接口中一共就包含两个方法分别是返回过期时间和返回前缀。
public interface KeyPrefix{
int getExpireSeconds();//过期时间
String getPrefix();
}
为了实现一些通用的方法,我们定义BasePrefix抽象类,提供两个不同参数的构造器,并且实现getPrefix方法为拼接类名+prefix,因为类名肯定是唯一的,所以就算后面的prefix冲突,也不会造成重复:
public abstract class BasePrefix implements KeyPrefix {
private int expireSeconds;
private String prefix;
public BasePrefix( int expireSeconds, String prefix) {
this.expireSeconds = expireSeconds;
this.prefix = prefix;
}
public BasePrefix(String prefix) {//0代表永不过期
this(0, prefix);
}
@Override
public int getExpireSeconds() {
return expireSeconds;
}
@Override
public String getPrefix() {
return getClass().getSimpleName()+":"+prefix;
}
}
之后就可以定义每个模块单独的Key了,这里以UserKey为例:
public class UserKey extends BasePrefix{
public UserKey(String prefix) {
super(prefix);
}
public static UserKey getById = new UserKey("id");
public static UserKey getByName = new UserKey("name");
}
这种实现方式的好处是每个模块用到的Key都能在自己的类中单独定义,而且肯定不会造成冲突。
解决了Key生成器之后,就可以开始封装jedis通用的方法了,新建RedisService类,并注入JedisPool bean
@Service
public class RedisService {
@Autowired
JedisPool jedisPool;
}
首先最通用的两个方法肯定是set和get,set的基本思路是根据KeyPrefix+key来生成实际的redis的key,然后将需要保存的对象通过fastJson的toJSONString方法转换为String,然后保存到redis。
//将目标对象序列化为String
private <T> String beanToString(T value) {
if(value == null) {
return null;
}
Class<?> clazz = value.getClass();
//基础类型不需要json序列化
if(clazz == int.class || clazz == Integer.class) {
return ""+value;
}else if(clazz == String.class) {
return (String)value;
}else if(clazz == long.class || clazz == Long.class) {
return ""+value;
}else {
return JSON.toJSONString(value);
}
}
public <T> boolean set(KeyPrefix prefix, String key, T value){
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String str = beanToString(value);
if(Strings.isNullOrEmpty(str)) {
return false;
}
//生成真正的key
String realKey = prefix.getPrefix() + key;
int seconds = prefix.getExpireSeconds();
if(seconds <= 0) {
jedis.set(realKey, str);
}else {
//设置过期时间
jedis.setex(realKey, seconds, str);
}
return true;
}finally {
//返回给jedisPoll连接池
returnToPool(jedis);
}
}
同样,get方法是set的逆过程:
private <T> T stringToBean(String str, Class<T> clazz) {
if(Strings.isNullOrEmpty(str) || clazz == null) {
return null;
}
if(clazz == int.class || clazz == Integer.class) {
return (T)Integer.valueOf(str);
}else if(clazz == String.class) {
return (T)str;
}else if(clazz == long.class || clazz == Long.class) {
return (T)Long.valueOf(str);
}else {
return JSON.toJavaObject(JSON.parseObject(str), clazz);
}
}
public <T> T get(KeyPrefix prefix, String key, Class<T> clazz){
Jedis jedis = null;
try{
jedis = jedisPool.getResource();
String realKey = prefix.getPrefix()+key;
String str = jedis.get(realKey);
T t = stringToBean(str,clazz);
return t;
}finally {
returnToPool(jedis);
}
}
除了set和get方法之外,还有一些别的方法比如判断key是否存在,自增,自减等运算:
public boolean exists(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.exists(realKey);
}finally {
returnToPool(jedis);
}
}
public Long incr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.incr(realKey);
}finally {
returnToPool(jedis);
}
}
public Long decr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.decr(realKey);
}finally {
returnToPool(jedis);
}
}
到这里,jedis的一些常用操作已经全部封装完毕,下面来测试一下Key生成器和封装的方法好不好用
定义实体类User.groovy和前端控制器UserController
class User {
Long id
String name
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
RedisService redisService;
@RequestMapping("/get/{id}")
public User redisGet(@PathVariable("id") Long id) {
User user = redisService.get(UserKey.getById, "" + id, User.class);
return user;
}
@RequestMapping("/set/{id}")
public Boolean redisSet(@PathVariable("id")Long id) {
User user = new User();
user.setId(id);
user.setName("name"+id);
redisService.set(UserKey.getById,""+id,user);
return true;
}
}
启动项目,访问localhost:8080/user/set/1 ,结果如图,然后从命令行查看一下redis:
可以看到user对象已经成功保存到redis中了,key为UserKey:id1,可以分析一下,因为我们使用的是set(UserKey.getById,id,user)
;,所以user对象的前缀是UserKey:id,然后拼接上key为传入的id的值1,所以最终的key为UserKey:id1,还是比较简洁明了的
下面测试一下前端能否取到redis中的user对象,访问localhost:8080/user/get/1,可以看到确实也能取到:
以上就是springboot整合redis作为k-v数据库的全部步骤。之后可以围绕这一节的内容做其他功能,比如分布式session等。
本篇博客参考自慕课网的在线课程:https://coding.imooc.com/class/168.html