美文网首页
第十章:springboot2 配置Redis和缓存

第十章:springboot2 配置Redis和缓存

作者: 菜出意料 | 来源:发表于2023-12-10 10:03 被阅读0次

    springboot版本:2.7.6

    集成redis

    1. 添加依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- 单节点设置启用连接池时添加 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    

    2. 添加配置

    spring:
      redis:
        database: 0
        password: yx123123=
        host: 192.168.110.108
        port: 6379
        client-type: lettuce
        lettuce:
          pool:
        # 连接耗尽时,最多等待10秒
            max-wait: 10s
        # 连接池中最小空闲连接数
            min-idle: 4
        # 空闲对象逐出器运行间隔时长
            time-between-eviction-runs: 10s
        # 启用池
            enabled: true
    

    3. Java Configuration

    /** 
     * redis 连接工厂
     */
     @Autowired
    private RedisConnectionFactory connectionFactory;
    
    /**
     * 实例化RedisTemplate,使用Json序列化,并对日期/时间进行格式化,统一使用字符串key
     *
     * @return RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        ObjectMapper objectMapper = new ObjectMapper();
        // 日期格式支持
        JavaTimeModule timeModule = new JavaTimeModule();
        final ZoneId DEFAULT_ZONE_ID = ZoneId.of("CTT", SHORT_IDS);
        final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(DEFAULT_ZONE_ID);
        final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DEFAULT_ZONE_ID);
        timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER)).addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER)).addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER)).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
        objectMapper.registerModule(timeModule);
        # 反序列化时忽略对象中不存在的属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        # 反序列化时可以转为对象
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
        // key统一使用字符串
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // value统一使用json
        template.setValueSerializer(genericJackson2JsonRedisSerializer);
        template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        template.setConnectionFactory(connectionFactory);
        return template;
    }
    

    集成cache

    1. 添加依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    

    2. 添加配置

    spring:  
      cache:
        type: REDIS
        redis:
          # 配置缓存时长15分钟
          time-to-live: 15m
          cache-null-values: false
          enable-statistics: true
          # 缓存key的前缀
          key-prefix: 'cache:${spring.application.name}:'
    

    3. 代码

    3.1 Java Configuration

    @EnableCaching
    @Configuration
    @EnableConfigurationProperties(CacheProperties.class)
    public class RedisCacheAutoConfiguration extends CachingConfigurerSupport {
        /**
         * redis 连接工厂
         */
        private final RedisConnectionFactory connectionFactory;
    
        /**
         * 缓存配置数据
         */
        private final CacheProperties cacheProperties;
    
        public RedisCacheAutoConfiguration(RedisConnectionFactory connectionFactory, CacheProperties cacheProperties) {
            this.connectionFactory = connectionFactory;
            this.cacheProperties = cacheProperties;
        }
    
    
        @Bean
        @Override
        public CacheManager cacheManager() {
            CacheProperties.Redis redisCacheProperties = cacheProperties.getRedis();
            RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(connectionFactory).cacheDefaults(redisCacheConfiguration(redisCacheProperties)).transactionAware().cacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000)));
            if (redisCacheProperties.isEnableStatistics()) {
                builder.enableStatistics();
            }
            return builder.build();
        }
    
        /**
         * 自定义KeyGenerator
         * key结构 key-prefix+cacheName::类名.方法名(参数)
         * 最终生成的key会受到key-prefix和@Cacheable注解中配置的cacheNames影响 示例:
         * <b>@Cacheable(cacheNames = {"query.hello"})</b>
         * query.hello::HelloController.hello(spring)
         * 如果配置文件中配置了key-prefix: 'cache:'
         * cache:query.hello::HelloController.hello(world)
         *
         * @return KeyGenerator
         */
        @Bean
        @Override
        public KeyGenerator keyGenerator() {
            return (target, method, params) -> {
                String paramStr = Stream.of(params).map(Object::toString).collect(Collectors.joining(",", "(", ")"));
                return target.getClass().getSimpleName() + "." + method.getName() + paramStr;
            };
        }
    
        /**
         * redis cache configuration 配置
         * 如果需要缓存LocalDateTime类型,自定义value序列化器
         *
         * @param redisCacheProperties redis缓存配置
         * @return RedisCacheConfiguration
         */
        RedisCacheConfiguration redisCacheConfiguration(CacheProperties.Redis redisCacheProperties) {
            RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new RedisCacheValueSerializer()));
            if (redisCacheProperties.getTimeToLive() != null) {
                cacheConfig = cacheConfig.entryTtl(redisCacheProperties.getTimeToLive());
            }
            if (redisCacheProperties.getKeyPrefix() != null) {
                cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
            }
            if (!redisCacheProperties.isCacheNullValues()) {
                cacheConfig = cacheConfig.disableCachingNullValues();
            }
            if (!redisCacheProperties.isUseKeyPrefix()) {
                cacheConfig = cacheConfig.disableKeyPrefix();
            }
            // 这里会影响最终生成的缓存key
            if (StringUtils.hasText(redisCacheProperties.getKeyPrefix())) {
                cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
            }
            return cacheConfig;
        }
    }
    

    3.2 自定义value序列化器

    /**
     * spring cache redis 序列化器
     * 用redis缓存spring cache时使用,解决了LocalDateTime序列化和反序列化问题
     *
     * @author co
     * @since 2023-12-05 16:37:00
     */
    public class RedisCacheValueSerializer extends GenericJackson2JsonRedisSerializer {
        public RedisCacheValueSerializer() {
            super();
        }
    
        @Override
        public Object deserialize(byte[] source) throws SerializationException {
            Object value = super.deserialize(source);
            if (value instanceof String) {
                String str = value == null ? "" : (String) value;
                // 以下为LocalDate和LocalDateTime的判断和转换,可以替换为其他更具效率的方式
                try {
                    if (str.length() == 10) {
                        return LocalDate.parse(str, DATE_FORMATTER);
                    }
                    if (str.length() == 19) {
                        return LocalDateTime.parse(str, DATE_TIME_FORMATTER);
                    }
                } catch (DateTimeParseException ex) {
    
                }
            }
            return value;
        }
    
        @Override
        public byte[] serialize(Object source) throws SerializationException {
            if (source instanceof LocalDateTime) {
                return super.serialize(((LocalDateTime) source).format(DATE_TIME_FORMATTER));
            }
    
            if (source instanceof LocalDate) {
                return super.serialize(((LocalDate) source).format(DATE_FORMATTER));
            }
            return super.serialize(source);
        }
    }
    

    完整示例

    1. pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- 单节点设置启用连接池时添加 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <!-- spring cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    

    2. yaml

    spring:
      application:
        name: app-name
      cache:
        type: REDIS
        redis:
          time-to-live: 15m
          cache-null-values: false
          enable-statistics: true
          key-prefix: 'cache:${spring.application.name}:'
      redis:
        database: 0
        password: yx123123=
        host: 192.168.110.108
        client-type: lettuce
        lettuce:
          pool:
            max-wait: 10s
            min-idle: 4
            time-between-eviction-runs: 10s
            enabled: true
    

    3. Java代码

    3.1 config

    /**
     * Redis & Cache相关配置类
     * 配置了自定义KeyGenerator,因此必须继承CachingConfigurerSupport,否则不生效
     *
     * @author co
     * @since 2023-12-05 10:19:48
     */
    @EnableCaching
    @Configuration
    @Import({RedisClient.class})
    @EnableConfigurationProperties(CacheProperties.class)
    public class RedisCacheAutoConfiguration extends CachingConfigurerSupport {
        /**
         * redis 连接工厂
         */
        private final RedisConnectionFactory connectionFactory;
    
        /**
         * 缓存配置数据
         */
        private final CacheProperties cacheProperties;
    
        public RedisCacheAutoConfiguration(RedisConnectionFactory connectionFactory, CacheProperties cacheProperties) {
            this.connectionFactory = connectionFactory;
            this.cacheProperties = cacheProperties;
        }
    
        /**
         * 实例化RedisTemplate,使用Json序列化,并对日期/时间进行格式化,统一使用字符串key
         *
         * @return RedisTemplate
         */
        @Bean
        public RedisTemplate<String, Object> redisTemplate() {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            ObjectMapper objectMapper = new ObjectMapper();
            // 日期格式支持
            final ZoneId DEFAULT_ZONE_ID = ZoneId.of("CTT", SHORT_IDS);
            final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(DEFAULT_ZONE_ID);
            final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DEFAULT_ZONE_ID);
            JavaTimeModule timeModule = new JavaTimeModule();
            timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER)).addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER)).addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER)).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
            objectMapper.registerModule(timeModule);
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
            GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
            // key统一使用字符串
            template.setKeySerializer(RedisSerializer.string());
            template.setHashKeySerializer(RedisSerializer.string());
            // value统一使用json
            template.setValueSerializer(genericJackson2JsonRedisSerializer);
            template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
            template.setConnectionFactory(connectionFactory);
            return template;
        }
    
        @Bean
        @Override
        public CacheManager cacheManager() {
            CacheProperties.Redis redisCacheProperties = cacheProperties.getRedis();
            RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(connectionFactory).cacheDefaults(redisCacheConfiguration(redisCacheProperties)).transactionAware().cacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, BatchStrategies.scan(1000)));
            if (redisCacheProperties.isEnableStatistics()) {
                builder.enableStatistics();
            }
            return builder.build();
        }
    
        /**
         * 自定义KeyGenerator
         * key结构 key-prefix+cacheName::类名.方法名(参数)
         * 最终生成的key会受到key-prefix和@Cacheable注解中配置的cacheNames影响 示例:
         * <b>@Cacheable(cacheNames = {"query.hello"})</b>
         * query.hello::HelloController.hello(spring)
         * 如果配置文件中配置了key-prefix: 'cache:'
         * cache:query.hello::HelloController.hello(world)
         *
         * @return KeyGenerator
         */
        @Bean
        @Override
        public KeyGenerator keyGenerator() {
            return (target, method, params) -> {
                String paramStr = Stream.of(params).map(Object::toString).collect(Collectors.joining(",", "(", ")"));
                return target.getClass().getSimpleName() + "." + method.getName() + paramStr;
            };
        }
    
        /**
         * redis cache configuration 配置
         * 如果需要缓存LocalDateTime等类型,需要在pom.xml中引入以下依赖,可根据错误提示进行配置
         * <dependency>
         * <groupId>com.fasterxml.jackson.datatype</groupId>
         * <artifactId>jackson-datatype-jsr310</artifactId>
         * </dependency>
         *
         * @param redisCacheProperties redis缓存配置
         * @return RedisCacheConfiguration
         */
        RedisCacheConfiguration redisCacheConfiguration(CacheProperties.Redis redisCacheProperties) {
            RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new RedisCacheValueSerializer()));
            if (redisCacheProperties.getTimeToLive() != null) {
                cacheConfig = cacheConfig.entryTtl(redisCacheProperties.getTimeToLive());
            }
            if (redisCacheProperties.getKeyPrefix() != null) {
                cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
            }
            if (!redisCacheProperties.isCacheNullValues()) {
                cacheConfig = cacheConfig.disableCachingNullValues();
            }
            if (!redisCacheProperties.isUseKeyPrefix()) {
                cacheConfig = cacheConfig.disableKeyPrefix();
            }
            // 这里会影响最终生成的缓存key
            if (StringUtils.hasText(redisCacheProperties.getKeyPrefix())) {
                cacheConfig = cacheConfig.prefixCacheNameWith(redisCacheProperties.getKeyPrefix());
            }
            return cacheConfig;
        }
    }
    

    3.2 redis工具类

    /**
     * Redis工具类
     * <p>
     * 包含常用操作及获取锁
     *
     * @author co 
     * @since 2023-12-07 10:05:20
     */
    public final class RedisClient {
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
        /**
         * 指定缓存失效时间
         * (命令EXPIRE)
         *
         * @param key  键
         * @param time 时间(秒)
         * @return true / false
         */
        public Boolean expire(String key, int time) {
            if (time > 0) {
                return redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return false;
        }
    
        /**
         * 指定缓存失效时间
         * (命令EXPIRE)
         *
         * @param key      键
         * @param time     时间(秒)
         * @param timeUnit 时间单位
         * @return true / false
         */
        public Boolean expire(String key, long time, TimeUnit timeUnit) {
            if (time > 0) {
                return redisTemplate.expire(key, time, timeUnit);
            }
            return false;
        }
    
        /**
         * 根据 key 获取过期时间
         * (命令TTL)
         *
         * @param key 键
         * @return 秒
         */
        public Long getExpire(String key) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        }
    
        /**
         * 判断 key 是否存在
         * (命令EXISTS)
         *
         * @param key 键
         * @return true / false
         */
        public Boolean exists(String key) {
            return redisTemplate.hasKey(key);
        }
    
        /**
         * 删除缓存
         * 不存在key不会抛异常
         *
         * @param keys 集合
         */
        public void delete(Collection<String> keys) {
            redisTemplate.delete(keys);
        }
    
    
        /**
         * 删除缓存
         * (命令DEL)
         *
         * @param key 键(一个或者多个)
         */
        public void delete(String... key) {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                } else {
                    redisTemplate.delete(Arrays.asList(key));
                }
            }
        }
    
        /**
         * 按表达式删除缓存
         *
         * @param pattern 表达式
         */
        public void deleteAll(String pattern) {
            if (pattern == null || pattern.length() == 0) {
                throw new IllegalArgumentException("参数不正确");
            }
            redisTemplate.delete(scan(pattern));
        }
    
        /**
         * 查询所有满足表达式的key
         * (命令KEYS,实际是利用SCAN命令实现,不会产生阻塞)
         *
         * @param pattern 表达式
         * @return key
         */
        @SuppressWarnings("all")
        @NonNull
        public Set<String> scan(String pattern) {
            if (pattern == null || pattern.length() == 0) {
                throw new IllegalArgumentException("参数不正确");
            }
            return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
                        Set<String> keys = new HashSet<>();
                        Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(pattern).count(1000).build());
                        while (cursor.hasNext()) {
                            keys.add(new String(cursor.next()));
                        }
                        return keys;
                    }
            );
        }
    
    
    //    ============================== String ==============================
    
        /**
         * 放入缓存
         *
         * @param key   键
         * @param value 值
         */
        public void set(String key, Object value, long timeout, TimeUnit timeUnit) {
            redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
        }
    
        /**
         * 放入缓存
         *
         * @param key   键
         * @param value 值
         */
        public void set(String key, Object value) {
            redisTemplate.opsForValue().set(key, value);
        }
    
        /**
         * 获取缓存(string数据结构)
         * <p>
         * 通过泛型T指定缓存数据类型
         *
         * @param key 键
         * @return 值
         * @see #getMap
         */
        @SuppressWarnings("unchecked")
        public <T> T get(String key) {
            return key == null ? null : (T) redisTemplate.opsForValue().get(key);
        }
    
    
        /**
         * 缓存普通键值对,并设置失效时间
         *
         * @param key     键
         * @param value   值
         * @param seconds 时间(秒),如果 time <= 0 则不设置失效时间
         */
        public void set(String key, Object value, int seconds) {
            if (seconds > 0) {
                redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, seconds);
            }
        }
    
        /**
         * 当key不存在时放入键值对
         * 如果已经存在key返回false
         *
         * @param key   键
         * @param value 值
         * @return true/false
         */
        public Boolean setNx(String key, Object value) {
            return redisTemplate.opsForValue().setIfAbsent(key, value);
        }
    
        /**
         * 当key不存在时放入键值对,并在指定时间后自动删除
         * 如果已经存在key返回false
         *
         * @param key   键
         * @param value 值
         * @return true/false
         */
        public Boolean setNx(String key, Object value, int time) {
            return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
        }
    
        /**
         * 递增
         * 如果不存在key将自动创建
         * (命令INCR)
         *
         * @param key       键
         * @param increment 递增大小
         * @return 递增后的值
         */
        @SuppressWarnings("unchecked")
        public Long increment(String key, Long increment) {
            return redisTemplate.opsForValue().increment(key, increment);
        }
    
        /**
         * 递增1
         * 如果不存在key将自动创建
         * (命令INCR)
         *
         * @param key 键
         * @return 递增后的值
         */
        public Long increment(String key) {
            return redisTemplate.opsForValue().increment(key, 1);
        }
    
        /**
         * 递减
         * 如果不存在key将自动创建
         * (命令DECR)
         *
         * @param key       键
         * @param decrement 递减大小
         * @return 递减后的值
         */
        @SuppressWarnings("unchecked")
        public <T extends Number> T decrement(String key, T decrement) {
            return (T) redisTemplate.opsForValue().increment(key, -decrement.doubleValue());
        }
    
        /**
         * 递减1
         * 如果不存在key将自动创建
         * (命令DECR)
         *
         * @param key 键
         * @return 递减后的值
         */
        public long decrement(String key) {
            return decrement(key, 1L);
        }
    
    //    ============================== Map ==============================
    
        /**
         * 往指定HashMap中添加一对键值对,key不存在将自动创建
         * (命令HMSET)
         *
         * @param name  HashMap的名字
         * @param key   添加的键
         * @param value 添加的值
         */
        public void putMap(String name, String key, Object value) {
            redisTemplate.opsForHash().put(name, key, value);
        }
    
        /**
         * 往指定HashMap中添加一对键值对,key不存在将自动创建,key存在不进行任何操作
         * (命令HSETNX)
         *
         * @param name  HashMap的名字
         * @param key   添加的键
         * @param value 添加的值
         */
        public Boolean putMapIfAbsent(String name, String key, Object value) {
            return redisTemplate.opsForHash().putIfAbsent(name, key, value);
        }
    
        /**
         * 往指定HashMap中添加另一个map
         * (命令HMSET)
         *
         * @param name HashMap的名字
         * @param map  值
         */
        public void putMap(String name, Map<String, ?> map) {
            redisTemplate.opsForHash().putAll(name, map);
        }
    
        /**
         * 从指定HashMap中获取指定key的值
         * (命令HGET)
         *
         * @param mapName HashMap的名字(no null)
         * @param key     HashMap中的键(no null)
         * @param <T>     根据实际类型自定义
         * @return 值
         */
        @SuppressWarnings("unchecked")
        public <T> T getMapItem(String mapName, String key) {
            return (T) redisTemplate.opsForHash().get(mapName, key);
        }
    
        /**
         * 从指定HashMap中获取多个key的值
         * (命令HMGET)
         *
         * @param mapName HashMap的名字(no null)
         * @param key     HashMap中的键,传多个(no null)
         * @param <T>     根据实际类型自定义
         * @return list
         */
        public <T> List<T> getMapItems(String mapName, String... key) {
            return redisTemplate.<String, T>opsForHash().multiGet(mapName, Arrays.asList(key));
        }
    
        /**
         * 获取指定的HashMap
         * (命令HGETALL)
         *
         * @param mapName HashMap的名字(no null)
         * @return HashMap
         */
        public <T> Map<String, T> getMap(String mapName) {
            return redisTemplate.<String, T>opsForHash().entries(mapName);
        }
    
    
        /**
         * 删除 HashMap 中的值
         * (命令HDEL)
         *
         * @param mapName HashMap的名字
         * @param keys    HashMap中的key(可以多个,no null)
         */
        public void deleteHash(String mapName, Object... keys) {
            redisTemplate.opsForHash().delete(mapName, keys);
        }
    
        /**
         * 判断指定 HashMap 中是否含有指定key
         *
         * @param hashMapName HashMap的名字(no null)
         * @param key         HashMap中的key(no null)
         * @return true / false
         */
        public Boolean exists(String hashMapName, String key) {
            return redisTemplate.opsForHash().hasKey(hashMapName, key);
        }
    
        /**
         * map 递增指定值
         *
         * @param hashMapName HashMap的名字(no null)
         * @param key         HashMap中的key(no null)
         * @return true / false
         */
        public Long incrementMap(String hashMapName, String key, long delta) {
            return redisTemplate.opsForHash().increment(hashMapName, key, delta);
        }
    
        /**
         * 是否存在hashkey
         *
         * @param hashMapName HashMap的名字(no null)
         * @param hashKey     HashMap中的key(no null)
         * @return true / false
         */
        public Boolean hasMapKey(String hashMapName, Object hashKey) {
            return redisTemplate.opsForHash().hasKey(hashMapName, hashKey);
        }
    
    
        //    ============================== zset ==============================
    
        /**
         * 添加zset元素 有则覆盖
         *
         * @param key
         * @param v
         * @param score
         * @return
         */
        public <T> Boolean zAdd(String key, T v, long score) {
            return redisTemplate.opsForZSet().add(key, v, score);
        }
    
        /**
         * 添加zset元素 有则覆盖
         *
         * @param key
         * @param start
         * @param end
         * @return
         */
        public Long zRemove(String key, long start, long end) {
            return redisTemplate.opsForZSet().removeRange(key, start, end);
        }
    
        /**
         * zset 元素个数
         *
         * @param key
         * @return
         */
        public Long zSize(String key) {
            return redisTemplate.opsForZSet().size(key);
        }
    
        /**
         * 自增zset score
         *
         * @param key
         * @param v
         * @param delta
         * @return
         */
        public <T> Double zIncrementScore(String key, T v, Double delta) {
            return redisTemplate.opsForZSet().incrementScore(key, v, delta);
        }
    
        /**
         * 获取指定key的所有元素
         *
         * @param key
         * @param <T>
         * @return
         */
        public <T> Set<T> zAll(String key) {
            return (Set<T>) redisTemplate.opsForZSet().range(key, 0, -1);
        }
    
        /**
         * 获取指定位置元素
         *
         * @param key
         * @param index
         * @param <T>
         * @return
         */
        public <T> T zGet(String key, int index) {
            Set<Object> objects = redisTemplate.opsForZSet().range(key, index, index + 1L);
            if (CollectionUtils.isEmpty(objects)) return null;
            return (T) objects.iterator().next();
        }
    
        /**
         * zset删除指定元素
         *
         * @param key
         * @param v
         * @return
         */
        public Long zDel(String key, Object v) {
            return redisTemplate.opsForZSet().remove(key, v);
        }
    
        //    ============================== set ==============================
    
        public Boolean sIsMember(String key, Object obj) {
            return redisTemplate.opsForSet().isMember(key, obj);
        }
    
        /**
         * 新增set 元素
         *
         * @param key
         * @param v
         * @return
         */
        public <T> Long sAdd(String key, T v) {
            return redisTemplate.opsForSet().add(key, v);
        }
    
        /**
         * 获取set元素
         *
         * @param key
         * @return
         */
        public <T> Set<T> sGet(String key) {
            return (Set<T>) redisTemplate.opsForSet().members(key);
        }
    
        /**
         * 新增set元素
         *
         * @param key
         * @param values
         */
        public <T> void sAdd(String key, T... values) {
            redisTemplate.opsForSet().add(key, values);
        }
    
        /**
         * 移除set元素
         *
         * @param key
         * @param v
         */
        public <T> void srm(String key, T v) {
            redisTemplate.opsForSet().remove(key, v);
        }
    
        //    ============================== list ==============================
    
        /**
         * 从左边塞入元素
         *
         * @param key
         * @param v
         */
        public <T> Long lPush(String key, T v) {
            return redisTemplate.opsForList().leftPush(key, v);
        }
    
        /**
         * 从右边批量塞入元素
         *
         * @param key
         * @param vs
         */
        public <T> Long rPushAll(String key, T... vs) {
            return redisTemplate.opsForList().rightPushAll(key, vs);
        }
    
        /**
         * 删除右边的元素
         *
         * @param key
         * @param removeCount
         */
        public void rightRemove(String key, Long removeCount) {
            redisTemplate.opsForList().trim(key, 0, listSize(key) - removeCount);
        }
    
        /**
         * list集合中元素个数
         *
         * @param key
         */
        public Long listSize(String key) {
            return redisTemplate.opsForList().size(key);
        }
    
        /**
         * 批量查询
         *
         * @param key
         */
        public <T> List<T> list(String key) {
            return (List<T>) redisTemplate.opsForList().range(key, 0, -1);
        }
    
        /**
         * 获取redisTemplate,可实现更多操作
         *
         * @return RedisTemplate
         */
        public RedisTemplate<String, Object> getRedisTemplate() {
            return redisTemplate;
        }
    
        public <T> T execute(RedisScript<T> script, List<String> keys, Object... args) {
            return redisTemplate.execute(script, keys, args);
        }
    
        /**
         * 管道
         *
         * @param consumer
         * @param <T>
         * @return
         */
        @SuppressWarnings("unchecked")
        public <T> List<T> piPipelined(Consumer<RedisOperations<Object, Object>> consumer) {
            return (List<T>) redisTemplate.executePipelined(new SessionCallback<Object>() {
                @Override
                public Object execute(RedisOperations operations) throws DataAccessException {
                    consumer.accept(operations);
                    return null;
                }
            });
        }
    
    
        //    ============================== bitmap ==============================
    
        /**
         * bitmap设置指定位置 为 1或0
         * (命令 SETBIT)
         *
         * @param key    key
         * @param offset 偏移量
         * @param value  true / false
         */
        public boolean setBit(String key, long offset, boolean value) {
            return redisTemplate.opsForValue().setBit(key, offset, value);
        }
    
        /**
         * bitmap获取指定位置结果
         * (命令 GETBIT)
         *
         * @param key    key
         * @param offset 偏移量
         * @return true / false
         */
        public boolean getBit(String key, long offset) {
            return redisTemplate.opsForValue().getBit(key, offset);
        }
    
        /**
         * bitmap 统计和,所有位置为1的总数量
         * (命令 BITCOUNT)
         *
         * @param key key
         */
        public long bitCount(String key) {
            return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
        }
    
        public long bitPos(String key, boolean bit, Range<Long> range) {
            return redisTemplate.execute((RedisCallback<Long>) con -> con.bitPos(key.getBytes(), bit, range));
        }
    
        /**
         * bitField
         * (命令 BITFIELD)
         *
         * @param key                 key
         * @param bitFieldSubCommands demo: BitFieldSubCommands bitFieldSubCommands =BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.UINT_16).valueAt(0);
         */
        public List<Long> bitField(String key, BitFieldSubCommands bitFieldSubCommands) {
            return redisTemplate.opsForValue().bitField(key, bitFieldSubCommands);
        }
    
        @Nullable
        public RedisConnection getConnection() {
            final RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
            if (connectionFactory == null) return null;
            return connectionFactory.getConnection();
        }
    }
    

    3.3 自定义缓存值序列化器

    /**
     * spring cache redis 序列化器
     * 用redis缓存spring cache时使用,解决了LocalDateTime序列化和反序列化问题
     *
     * @author co
     * @since 2023-12-05 16:37:00
     */
    public class RedisCacheValueSerializer extends GenericJackson2JsonRedisSerializer {
        public RedisCacheValueSerializer() {
            super();
        }
    
        @Override
        public Object deserialize(byte[] source) throws SerializationException {
            Object value = super.deserialize(source);
            if (value instanceof String) {
                String str = value == null ? "" : (String) value;
                // 以下为LocalDate和LocalDateTime的判断和转换,可以替换为其他更具效率的方式
                try {
                    if (str.length() == 10) {
                        return LocalDate.parse(str, DATE_FORMATTER);
                    }
                    if (str.length() == 19) {
                        return LocalDateTime.parse(str, DATE_TIME_FORMATTER);
                    }
                } catch (DateTimeParseException ex) {
    
                }
            }
            return value;
        }
    
        @Override
        public byte[] serialize(Object source) throws SerializationException {
            if (source instanceof LocalDateTime) {
                return super.serialize(((LocalDateTime) source).format(DATE_TIME_FORMATTER));
            }
    
            if (source instanceof LocalDate) {
                return super.serialize(((LocalDate) source).format(DATE_FORMATTER));
            }
            return super.serialize(source);
        }
    }
    

    相关文章

      网友评论

          本文标题:第十章:springboot2 配置Redis和缓存

          本文链接:https://www.haomeiwen.com/subject/wvyjgdtx.html