依赖jar包

引入包 版本
jdk 1.8
spring boot 2.7.4
spring-boot-starter-data-redis 2.7.4
caffeine 2.9.3
allbs-common 1.1.8
jackson-databind 2.13.4.2
micrometer-core 1.9.4

使用

添加依赖

<dependency>
  <groupId>cn.allbs</groupId>
  <artifactId>allbs-cache</artifactId>
  <version>1.1.8</version>
</dependency>
implementation 'cn.allbs:allbs-cache:1.1.8'
implementation("cn.allbs:allbs-cache:1.1.8")

说明

集成了一级caffeine 和二级redisred 缓存联合使用, key序列化方式为StringRedisSerializer value 序列化方式为GenericJackson2JsonRedisSerializer

开启二级缓存

添加注解@EnableAllbsCache

缓存核心配置示例

spring:
    cache:
        multi:
          # 指定需要缓存的key
          cache-names:
            - allbs
          # 是否存储空值,默认true,防止缓存穿透
          cache-null-values: true
          # 是否动态根据cacheName创建Cache的实现,默认true
          dynamic: true
          # 缓存key的前缀
          cache-prefix: ds
          caffeine:
            # 是否开启caffeine缓存默认为true
            enable: false
            # 访问后过期时间
            expire-after-access: 20m
            # 写入后过期时间
            expire-after-write: 20m
            # 写入后刷新时间
            refresh-after-write: 20m
            # 初始化大小
            initial-capacity: 20
            # 最大缓存对象个数,超过此数量时之前放入的缓存将失效
            maximum-size: 100
          redis:
            # 是否开启redis缓存默认为true
            enable: true
            # 全局过期时间Duration,15s代表过期时间15秒,默认为0不过期
            default-expiration: 15s
            # 全局空值过期时间Duration,默认和有值的一致
            default-null-values-expiration: 15s
            # 每个cacheName的过期时间,优先级比defaultExpiration高
            expires: {allbs: 15s, testds:60h}
            # 缓存更新时通知其他节点的topic名称
            topic: cache:redis:caffeine:topic

缓存使用

加入缓存

// 添加缓存
@Cacheable(value = CacheConstants.TEST_2, key = "'test'")
@GetMapping("/test")
public String test() {
    // 第二次将直接读取缓存,不会进入该方法
    String randomStr = RandomUtil.randomString(5);
    redisTemplate.opsForValue().set(CacheConstants.TEST_2_1, randomStr);
    log.info("返回结果将永远是第一次随机的数据,除非删除redis中的该key" + CacheConstants.TEST_2_1);
    return randomStr;
}

加入缓存,但是每次仍然进入方法内部

// 添加注解
@CachePut

移出缓存

// 添加注解
@CacheEvict

一个方法或者类上同时指定多个Spring Cache相关的注解

@Caching

redis配置示例

yml中添加如下配置,不用在启动类添加注解@EnableAllbsCache

spring:
  redis:
    database: 0
    host: ${REDIS-HOST:cq-server}
    port: 6379
    password: ${REDIS-PWD:111111}
    timeout: 5000
    lettuce:
      pool:
        min-idle: 2

自定义序列化方式说明

考虑到项目中实际使用需要使用的序列化方式与缓存不同,所以不再像此前版本定义好redis基础操作的序列化方式,由项目自主决定。同时添加配置可自定义当前项目的缓存的序列化方式。

import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 类 RedisTemplateConfig
 * </p>
 *
 * @author ChenQi
 * @since 2022/10/30 14:49
 */
@Configuration
@AllArgsConstructor
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisTemplateConfig {

    /**
     * 用于设置redis基础操作的序列化方式
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        // 使用Jackson2JsonRedisSerialize 替换默认jdk序列化明文储存
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

    /**
     * 用于设置缓存中的序列化方法
     * @param redisConnectionFactory
     * @return
     */
    @Bean(name = "stringKeyRedisTemplate")
    public RedisTemplate<Object, Object> stringKeyRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        // 使用Jackson2JsonRedisSerialize 替换默认序列化会明文储存
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

消息监听

只限于轻量化使用,大规模场景请使用rocketmq、rabbitmq等

方法一

自定义topic, 继承KeyspaceEventMessageListener类并实现ApplicationEventPublisherAware接口。重写doRegister、doHandleMessage、setApplicationEventPublisher方法。多个topic监听可以定义一个Topic List并放到addMessageListener中去即可。

public class CustomMessageListener extends KeyspaceEventMessageListener implements ApplicationEventPublisherAware {
    private static final Topic KEYEVENT_EXPIRED_TOPIC = new ChannelTopic("自定义的topic名称");
 
    @Nullable
    private ApplicationEventPublisher publisher;
 
    public CustomMessageListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }
 
    @Override
    protected void doRegister(RedisMessageListenerContainer listenerContainer) {
        listenerContainer.addMessageListener(this, KEYEVENT_EXPIRED_TOPIC);
    }
 
    @Override
    protected void doHandleMessage(Message message) {
        this.publishEvent(new RedisKeyExpiredEvent(message.getBody()));
    }
 
    protected void publishEvent(RedisKeyExpiredEvent event) {
        if (this.publisher != null) {
            this.publisher.publishEvent(event);
        }
 
    }
 
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }
}

方法二

接收方式
@Configuration
@RequiredArgsConstructor
@Slf4j
public class CustomMessageListenerHandle {
 
    /**
     * 方法功能:
     *
     * @param redisConnectionFactory
     * @return org.springframework.data.redis.listener.RedisMessageListenerContainer
     * @date 2021/3/29 15:03
     */
    @Bean
    public RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        container.addMessageListener((message, bytes) -> {
            log.info("接收到监听消息");
            // 业务处理
        }, new ChannelTopic("customMq"));
        return container;
    }
}
发送方式
redisTemplate.convertAndSend("自定义的topic名称", "发送内容");

过期key监听

继承KeyExpirationEventMessageListener重写onMessage方法即可

过期key和消息监听服务如果集群部署,或者多个服务配置了相同的监听配置,会每个服务都消费一次!!注意业务场景实际使用

@Component
public class KeyExpirationListener extends KeyExpirationEventMessageListener {
 
    public KeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }
 
    @Override
    public void onMessage(Message message, @Nullable byte[] pattern) {
        String key = message.toString();
        System.out.println("监听到key:" + key + "过期");
    }
 
}

发号器,使用redis实现,用于自增的企业编码、站点编码等

注入

@Resource
private IdGeneratorUtil idGeneratorUtil;

使用

生成id(每日重置自增序列)generateYMDId(String key, int length)
/**
    * 生成id(每日重置自增序列)
    * 格式:日期 + 指定位位自增数
    * 如:20210804000001
    *
    * @param key    储存序号的redis key
    * @param length 生成的后缀长度
    * @return 日期 + 指定位位自增数序号
    */
生成指定开头的自增序列 generatePreId(String key, String prefix, int length)
/**
     * 生成指定开头的自增序列
     * 格式: 指定字符 + 指定位自增数
     * 如W00001
     *
     * @param key    redis 储存的key
     * @param prefix 指定的开头
     * @param length 格式化长度
     * @return 指定字符 + 指定位自增数
     */
生成指定开头的自增序列 generatePreIdStep(String key, String prefix, int length, int step)
/**
     * 生成指定开头的自增序列 
     * 格式: 指定字符 + 指定位自增数
     * 如W00001
     *
     * @param key    redis 储存的key
     * @param prefix 指定的开头
     * @param length 格式化长度
     * @return 指定字符 + 指定位自增数
     */
生成指定开头的自增序列 generatePreId(String key, String prefix, int length, int step)
/**
     * 生成指定开头的自增序列
     * 格式: 指定字符 + 指定位自增数
     * 如W00001
     *
     * @param key    redis 储存的key
     * @param prefix 指定的开头
     * @param length 格式化长度
     * @param step   所在步长
     * @return 指定字符 + 指定位自增数
     */
指定带有前缀的发号器初始值 initGenerate(String key, String prefix, int initialValue)
/**
     * 初始化发号器默认值
     *
     * @param key          redis 的key
     * @param prefix       序列化的编码起始字符串
     * @param initialValue 初始值
     */
指定发号器初始值 initGenerate(String key, int initialValue)
/**
     * 指定发号器初始值
     *
     * @param key          redis 的key
     * @param initialValue 初始值
     */