springboot整合redis作为K-V数据库

springboot整合redis作为K-V数据库

redis简介

springboot整合redis

新建工程并配置jedis客户端

首先使用spring-initializr新建一个springboot项目,选上依赖为web。如下图所示:这里写图片描述

然后打开pom文件,添加jedisfastJsonguava的依赖:

<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类可以很方便的省略掉setget这种冗余代码。由于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

猜你喜欢

转载自blog.csdn.net/lengyuedanhen/article/details/79108068