Spring Boot整合Redis

Posted jian0110

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot整合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;
    }
View Code

 

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;
    }

}
View Code

 

三、测试

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;
    }
}
View Code

 

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 + ‘\\‘‘ +
                ‘}‘;
    }
}
View Code

 

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值

技术图片

 

以上是关于Spring Boot整合Redis的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot如何整合Redis

Spring Boot如何整合Redis

spring boot 整合 redis

Spring Boot整合Redis

Redis系列:Spring Boot整合Redisson

spring boot整合redis