欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Boot整合Redis的完整步骤

程序员文章站 2023-12-04 15:02:46
前言 实际 开发 中 缓存 处理是必须的,不可能我们每次客户端去请求一次 服务器 ,服务器每次都要去 数据库 中进行查找,为什么要使用缓存?说到底是为了提高系统的运行速度...

前言

实际 开发 中 缓存 处理是必须的,不可能我们每次客户端去请求一次 服务器 ,服务器每次都要去 数据库 中进行查找,为什么要使用缓存?说到底是为了提高系统的运行速度。将用户频繁访问的内容存放在离用户最近,访问速度最 快的 地方,提高用户的响 应速度,今天先来讲下在 springboot 中整合 redis 的详细步骤。

一、spring boot对redis的支持

spring对redis的支持是使用spring data redis来实现的,一般使用jedis或者lettuce(默认),java客户端在 org.springframework.boot.autoconfigure.data.redis(spring boot 2.x) 中redis的自动配置 autoconfiguredataredis

Spring Boot整合Redis的完整步骤 

redisautoconfiguration提供了redistemplate与stringredistemplate(只针对键值都是字符型的数据)模板,其中注解 @conditionalonmissingbean 是关键,表明该bean如果在spring中已经存在,则忽略,如果没有存在则在此处注册由spring管理,也就是说我们可以“重写”该bean,实现自己的redistemplate与stringredistemplate,事实上,是要需要重写的,理由如下:

  • 没有实现我们所需要的序列化;
  • 泛型总是<object, object>,大部分场景我们更需要<string, object>。
@bean
 @conditionalonmissingbean(
  name = {"redistemplate"}
 )
 public redistemplate<object, object> redistemplate(redisconnectionfactory redisconnectionfactory) throws unknownhostexception {
  redistemplate<object, object> template = new redistemplate();
  template.setconnectionfactory(redisconnectionfactory);
  return template;
 }

 @bean
 @conditionalonmissingbean
 public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory) throws unknownhostexception {
  stringredistemplate template = new stringredistemplate();
  template.setconnectionfactory(redisconnectionfactory);
  return template;
 }

二、实战

1、添加依赖

1)需要spring-boot-starter-cache依赖,管理缓存

<!-- spring boot cache -->
<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-cache</artifactid>
</dependency>

2)需要spring-boot-starter-data-redis依赖(注:spring boot 2.x改为在data下),支持redis:主要以为jedis客户端为主,排除默认的lettuce作为客户端的依赖

<!-- redis cache -->
<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-data-redis</artifactid>
  <!-- 排除lettuce包,使用jedis代替-->
  <exclusions>
    <exclusion>
      <groupid>io.lettuce</groupid>
      <artifactid>lettuce-core</artifactid>
    </exclusion>
  </exclusions>
</dependency>

3)需要jedis-client依赖(注:redis client 3版本以上会报错与spring-boot-starter-data-redis冲突,最好使用2.9.x),使用jedis作为客户端

<!-- redis client 3版本以上会报错与spring-boot-starter-data-redis冲突 -->
<dependency>
  <groupid>redis.clients</groupid>
  <artifactid>jedis</artifactid>
  <version>2.9.0</version>
</dependency>

2、redis配置

创建redisconfig配置类,增加@configuration注解,同时开启缓存管理支持(添加注解@enablecaching),继承cachingconfigurersupport重写key生成策略

@configuration
@enablecaching
public class redisconfig extends cachingconfigurersupport {
   /**
   * 生成key的策略:根据类名+方法名+所有参数的值生成唯一的一个key
   * @return
   */
  @bean
  @override
  public keygenerator keygenerator() {
    return (object target, method method, object... params) -> {
      stringbuilder sb = new stringbuilder();
      sb.append(target.getclass().getname());
      sb.append(method.getname());
      for (object obj : params) {
        sb.append(obj.tostring());
      }
      return sb.tostring();
    };
  }

}

之后使用的application.yml配置文件,其中这里已经选择jedis作为客户端。

# redis 配置
 redis:
   port: 6379
   # redis服务器连接密码(默认为空)
   password:
   host: xxx.xxx.xxx.xxx
   database: 0
   jedis:
    pool:
     #连接池最大连接数(使用负值表示没有限制)
     max-active: 300
     # 连接池中的最小空闲连接
     max-idle: 100
      # 连接池最大阻塞等待时间(使用负值表示没有限制)
     max-wait: 10000
     # 连接超时时间(毫秒)
     timeout: 5000

同时读取配置属性,注入jedispoolconfig

/**
   * redis配置属性读取
   */
  @value("${spring.redis.host}")
  private string host;
  @value("${spring.redis.port}")
  private int port;
  @value("${spring.redis.database}")
  private int database;
  @value("${spring.redis.jedis.pool.max-idle}")
  private int maxidle;
  @value("${spring.redis.jedis.pool.max-wait}")
  private long maxwaitmillis;
  @value("${spring.redis.jedis.pool.max-active}")
  private int maxactive;


  /**
   * jedispoolconfig配置
   * @return
   */
  @bean
  public jedispoolconfig jedispoolconfig() {
    log.info("初始化jedispoolconfig");
    jedispoolconfig jedispoolconfig = new jedispoolconfig();
    jedispoolconfig.setmaxtotal(maxactive);
    jedispoolconfig.setmaxwaitmillis(maxwaitmillis);
    jedispoolconfig.setmaxidle(maxidle);
    return jedispoolconfig;
  }

3、实现序列化

针对redistemplate或stringredistemplate进行序列化,同时重写注册bean

redistemplate默认使用jdkserializationredisserializer,stringredistmeplate默认使用的是stringredisserializer。但都是不符合实际要求的

/**
   * 重新实现redistemplate:解决序列化问题
   * @param redisconnectionfactory
   * @return
   */
  @bean
  @suppresswarnings({"rawtype", "unchecked"})
  public redistemplate<string, object> redistemplate(redisconnectionfactory redisconnectionfactory){
    redistemplate<string, object> template = new redistemplate();
    template.setconnectionfactory(redisconnectionfactory);
    jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
    objectmapper om = new objectmapper();
    // 设置任何字段可见
    om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
    // 设置不是final的属性可以转换
    om.enabledefaulttyping(objectmapper.defaulttyping.non_final);
    log.info("objectmapper: {}", om);
    jackson2jsonredisserializer.setobjectmapper(om);
    stringredisserializer stringredisserializer = new stringredisserializer();
    // key采用string的序列化方式
    template.setkeyserializer(stringredisserializer);
    // hash的key采用string的序列化方式
    template.sethashkeyserializer(stringredisserializer);
    // value序列化方式采用jackson序列化方式
    template.setvalueserializer(jackson2jsonredisserializer);
    // hash的value序列化方式采用jackson序列化方式
    template.sethashvalueserializer(jackson2jsonredisserializer);
    template.afterpropertiesset();
    template.setenabletransactionsupport(true);
    return template;
  }

  /**
   * 重新实现stringredistmeplate:键值都是string的的数据
   * @param redisconnectionfactory
   * @return
   */
  @bean
  public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory) {
    stringredistemplate template = new stringredistemplate();
    jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
    template.setconnectionfactory(redisconnectionfactory);
    stringredisserializer stringredisserializer = new stringredisserializer();
    // key采用string的序列化方式
    template.setkeyserializer(stringredisserializer);
    // hash的key采用string的序列化方式
    template.sethashkeyserializer(stringredisserializer);
    // value序列化方式采用jackson序列化方式
    template.setvalueserializer(jackson2jsonredisserializer);
    // hash的value序列化方式采用jackson序列化方式
    template.sethashvalueserializer(jackson2jsonredisserializer);
    return template;
  }

4、创建redis连接工厂,同时注册bean

注意spring boot 1.x与spring boot 2.x的区别,已在代码中注释表明,spring boot 1.x使用的是jedisconnectionfactory 。而spring boot 2.x使用的是redisstandaloneconfiguration ,之后传入jedisconnectionfactory返回bean

/**
   * 注入redisconnectionfactory
   * @return
   */
  @bean
  public redisconnectionfactory redisconnectionfactory(jedispoolconfig jedispoolconfig) {
    log.info("初始化jedisconnectionfactory");
    /* 在spring boot 1.x中已经过时,采用redisstandaloneconfiguration配置
    jedisconnectionfactory jedisconnectionfactory = new jedisconnectionfactory(jedispoolconfig);
    jedisconnectionfactory.sethostname(host);
    jedisconnectionfactory.setdatabase(database);*/

    // jedisconnectionfactory配置hsot、database、password等参数
    redisstandaloneconfiguration redisstandaloneconfiguration = new redisstandaloneconfiguration();
    redisstandaloneconfiguration.sethostname(host);
    redisstandaloneconfiguration.setport(port);
    redisstandaloneconfiguration.setdatabase(database);
    // jedisconnectionfactory配置jedispoolconfig
    jedisclientconfiguration.jedispoolingclientconfigurationbuilder jedispoolconfigbuilder =
        (jedisclientconfiguration.jedispoolingclientconfigurationbuilder)jedisclientconfiguration.builder();
    jedispoolconfigbuilder.poolconfig(jedispoolconfig);
    return new jedisconnectionfactory(redisstandaloneconfiguration);

  }

5、完整的redisconfig配置类

/**
 *
 * @author jian
 * @date 2019/4/14
 * @description
 * 1) redistemplate(或stringredistemplate)虽然已经自动配置,但是不灵活(第一没有序列化,第二泛型为<object, object>不是我们想要的类型)
 * 所以自己实现redistemplate或stringredistemplate)
 * 2) 采用rediscachemanager作为缓存管理器
 *
 */

@configuration
@enablecaching
public class redisconfig extends cachingconfigurersupport {

  private static final logger log = loggerfactory.getlogger(redisconfig.class);

  /**
   * redis配置属性读取
   */
  @value("${spring.redis.host}")
  private string host;
  @value("${spring.redis.port}")
  private int port;
  @value("${spring.redis.database}")
  private int database;
  @value("${spring.redis.jedis.pool.max-idle}")
  private int maxidle;
  @value("${spring.redis.jedis.pool.max-wait}")
  private long maxwaitmillis;
  @value("${spring.redis.jedis.pool.max-active}")
  private int maxactive;


  /**
   * jedispoolconfig配置
   * @return
   */
  @bean
  public jedispoolconfig jedispoolconfig() {
    log.info("初始化jedispoolconfig");
    jedispoolconfig jedispoolconfig = new jedispoolconfig();
    jedispoolconfig.setmaxtotal(maxactive);
    jedispoolconfig.setmaxwaitmillis(maxwaitmillis);
    jedispoolconfig.setmaxidle(maxidle);
    return jedispoolconfig;
  }

  /**
   * 注入redisconnectionfactory
   * @return
   */
  @bean
  public redisconnectionfactory redisconnectionfactory(jedispoolconfig jedispoolconfig) {
    log.info("初始化jedisconnectionfactory");
    /* 在spring boot 1.x中已经过时,采用redisstandaloneconfiguration配置
    jedisconnectionfactory jedisconnectionfactory = new jedisconnectionfactory(jedispoolconfig);
    jedisconnectionfactory.sethostname(host);
    jedisconnectionfactory.setdatabase(database);*/

    // jedisconnectionfactory配置hsot、database、password等参数
    redisstandaloneconfiguration redisstandaloneconfiguration = new redisstandaloneconfiguration();
    redisstandaloneconfiguration.sethostname(host);
    redisstandaloneconfiguration.setport(port);
    redisstandaloneconfiguration.setdatabase(database);
    // jedisconnectionfactory配置jedispoolconfig
    jedisclientconfiguration.jedispoolingclientconfigurationbuilder jedispoolconfigbuilder =
        (jedisclientconfiguration.jedispoolingclientconfigurationbuilder)jedisclientconfiguration.builder();
    jedispoolconfigbuilder.poolconfig(jedispoolconfig);
    return new jedisconnectionfactory(redisstandaloneconfiguration);

  }

  /**
   * 采用rediscachemanager作为缓存管理器
   * @param connectionfactory
   */
  @bean
  public cachemanager cachemanager(redisconnectionfactory connectionfactory) {
    rediscachemanager rediscachemanager = rediscachemanager.create(connectionfactory);
    return rediscachemanager;
  }


  /**
   * 生成key的策略:根据类名+方法名+所有参数的值生成唯一的一个key
   * @return
   */
  @bean
  @override
  public keygenerator keygenerator() {
    return (object target, method method, object... params) -> {
      stringbuilder sb = new stringbuilder();
      sb.append(target.getclass().getname());
      sb.append(method.getname());
      for (object obj : params) {
        sb.append(obj.tostring());
      }
      return sb.tostring();
    };
  }

  /**
   * 重新实现redistemplate:解决序列化问题
   * @param redisconnectionfactory
   * @return
   */
  @bean
  @suppresswarnings({"rawtype", "unchecked"})
  public redistemplate<string, object> redistemplate(redisconnectionfactory redisconnectionfactory){
    redistemplate<string, object> template = new redistemplate();
    template.setconnectionfactory(redisconnectionfactory);
    jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
    objectmapper om = new objectmapper();
    // 设置任何字段可见
    om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
    // 设置不是final的属性可以转换
    om.enabledefaulttyping(objectmapper.defaulttyping.non_final);
    log.info("objectmapper: {}", om);
    jackson2jsonredisserializer.setobjectmapper(om);
    stringredisserializer stringredisserializer = new stringredisserializer();
    // key采用string的序列化方式
    template.setkeyserializer(stringredisserializer);
    // hash的key采用string的序列化方式
    template.sethashkeyserializer(stringredisserializer);
    // value序列化方式采用jackson序列化方式
    template.setvalueserializer(jackson2jsonredisserializer);
    // hash的value序列化方式采用jackson序列化方式
    template.sethashvalueserializer(jackson2jsonredisserializer);
    template.afterpropertiesset();
    template.setenabletransactionsupport(true);
    return template;
  }

  /**
   * 重新实现stringredistmeplate:键值都是string的的数据
   * @param redisconnectionfactory
   * @return
   */
  @bean
  public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory) {
    stringredistemplate template = new stringredistemplate();
    jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
    template.setconnectionfactory(redisconnectionfactory);
    stringredisserializer stringredisserializer = new stringredisserializer();
    // key采用string的序列化方式
    template.setkeyserializer(stringredisserializer);
    // hash的key采用string的序列化方式
    template.sethashkeyserializer(stringredisserializer);
    // value序列化方式采用jackson序列化方式
    template.setvalueserializer(jackson2jsonredisserializer);
    // hash的value序列化方式采用jackson序列化方式
    template.sethashvalueserializer(jackson2jsonredisserializer);
    return template;
  }

}

三、测试

1、编写redis工具类

虽然redistemplate与stringredistemplate模板有提供的主要数据访问方法:

  • opsforvalue():操作只有简单属性的数据
  • opsforlist():操作含有list的数据
  • opsforset():操作含有set的数据
  • opsforhash():操作含有hash的数据
  • opsforzset():操作含有有序set类型zset的数据

但是相关比较抽象,实现起来比较复杂,有必要进一步封装,比如使用redistmeplate中的简单value的get操作:

object result = null;
valueoperations<serializable, object> operations = redistemplate.opsforvalue();
result = operations.get(key);

但是封装之后,相对客户端用户来说比较明了

/**
   * 读取缓存
   *
   * @param key
   * @return
   */
  public object get(final string key) {
    object result = null;
    valueoperations<serializable, object> operations = redistemplate.opsforvalue();
    result = operations.get(key);
    return result;
  }

完整的简单工具类如下:

@component
public class redisutils {


  @autowired
  private redistemplate redistemplate;

  /**
   * 批量删除对应的value
   *
   * @param keys
   */
  public void remove(final string... keys) {
    for (string key : keys) {
      remove(key);
    }
  }

  /**
   * 批量删除key
   *
   * @param pattern
   */
  public void removepattern(final string pattern) {
    set<serializable> keys = redistemplate.keys(pattern);
    if (keys.size() > 0) {
      redistemplate.delete(keys);
    }
  }

  /**
   * 删除对应的value
   *
   * @param key
   */
  public void remove(final string key) {
    if (exists(key)) {
      redistemplate.delete(key);
    }
  }

  /**
   * 判断缓存中是否有对应的value
   *
   * @param key
   * @return
   */
  public boolean exists(final string key) {
    return redistemplate.haskey(key);
  }

  /**
   * 读取缓存
   *
   * @param key
   * @return
   */
  public object get(final string key) {
    object result = null;
    valueoperations<serializable, object> operations = redistemplate.opsforvalue();
    result = operations.get(key);
    return result;
  }

  /**
   * 写入缓存
   *
   * @param key
   * @param value
   * @return
   */
  public boolean set(final string key, object value) {
    boolean result = false;
    try {
      valueoperations<serializable, object> operations = redistemplate.opsforvalue();
      operations.set(key, value);
      result = true;
    } catch (exception e) {
      e.printstacktrace();
    }
    return result;
  }

  /**
   * 写入缓存
   *
   * @param key
   * @param value
   * @return
   */
  public boolean set(final string key, object value, long expiretime) {
    boolean result = false;
    try {
      valueoperations<serializable, object> operations = redistemplate.opsforvalue();
      operations.set(key, value);
      redistemplate.expire(key, expiretime, timeunit.seconds);
      result = true;
    } catch (exception e) {
      e.printstacktrace();
    }
    return result;
  }
}

2、person实体类

需要注意的是一定要实现序列化,并且有序列化版本id

public class person implements serializable {
  private final long serialversionuid = 1l;

  private string id;
  private string name;
  private int age;
  private string gender;

  public string getid() {
    return id;
  }

  public void setid(string id) {
    this.id = id;
  }

  public string getname() {
    return name;
  }

  public void setname(string name) {
    this.name = name;
  }

  public int getage() {
    return age;
  }

  public void setage(int age) {
    this.age = age;
  }

  public string getgender() {
    return gender;
  }

  public void setgender(string gender) {
    this.gender = gender;
  }

  @override
  public string tostring() {
    return "person{" +
        "id='" + id + '\'' +
        ", name='" + name + '\'' +
        ", age=" + age +
        ", gender='" + gender + '\'' +
        '}';
  }
}

3、编写测试类

redis工具类spring已经做了管理(增加@compent注解),使用很简单,只需要注入redisutils即可

@runwith(springrunner.class)
@springboottest
public class redistest {

  @autowired
  private redisutils redisutils;

  @test
  public void test(){
    person person = new person();
    person.setage(23);
    person.setid("001");
    person.setname("zhangsan");
    redisutils.set("person-001", person);
    system.out.println(redisutils.get("person-001"));
  }

}

4、测试结果

在ide控制台中:

Spring Boot整合Redis的完整步骤 

在登录客户端后查看value值

 Spring Boot整合Redis的完整步骤

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。