Spring Boot整合Redis的完整步骤

 更新时间:2019年05月09日 08:53:32   作者:Jian  
这篇文章主要给大家介绍了关于Spring Boot整合Redis的完整步骤,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

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

 

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控制台中:

 

在登录客户端后查看value值

 

总结

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

相关文章

  • Java判断101-200之间有多少个素数并输出

    Java判断101-200之间有多少个素数并输出

    这篇文章主要介绍了Java判断101-200之间有多少个素数并输出,需要的朋友可以参考下
    2017-02-02
  • java高级用法之注解和反射讲义

    java高级用法之注解和反射讲义

    这篇文章主要给大家介绍了关于java高级用法之注解和反射讲义的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • 详解5种Java中常见限流算法

    详解5种Java中常见限流算法

    在高并发系统中,出于系统保护角度考虑,通常会对流量进行限流;不但在工作中要频繁使用,而且也是面试中的高频考点。本文就为大家整理了5种Java中常见限流算法,需要的可以参考一下
    2023-04-04
  • java教学笔记之对象的创建与销毁

    java教学笔记之对象的创建与销毁

    面向对象的编程语言使程序能够直观的反应客观世界的本来面目,并且使软件开发人员能够运用人类认识事物所采用的一般思维方法进行软件开发,是当今计算机领域中软件开发和应用的主流技术。
    2016-01-01
  • Java中比较抽象类与接口的异同

    Java中比较抽象类与接口的异同

    大家好,本篇文章主要讲的是Java中比较抽象类与接口的异同,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • 关于BeanUtils.copyProperties(source, target)的使用

    关于BeanUtils.copyProperties(source, target)的使用

    这篇文章主要介绍了关于BeanUtils.copyProperties(source, target)的使用说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • maven多模块pom配置实例详解

    maven多模块pom配置实例详解

    这篇文章主要介绍了maven多模块pom配置实例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-01-01
  • 详解Java设计模式之桥接模式

    详解Java设计模式之桥接模式

    桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯。桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。本文通过示例详细介绍了桥接模式的原理与使用,需要的可以参考一下
    2022-06-06
  • Java实现DES加解密算法解析

    Java实现DES加解密算法解析

    这篇文章主要介绍了Java实现DES加解密算法解析,结合完整实例形式分析了DES加密的相关原理,需要的朋友可以参考下。
    2016-10-10
  • Java中equals()方法重写实现代码

    Java中equals()方法重写实现代码

    这篇文章主要介绍了Java中equals()方法重写实现代码的相关资料,需要的朋友可以参考下
    2017-05-05

最新评论