SpringBoot中Cache缓存的使用总结

1. 简单示例

配置类,这里的@EnableCaching注解可以写在配置类,也可以写在启动类;

@EnableCaching
@Configuration
public class CacheConfig {
    @Primary
    @Bean
    @Qualifier("calculateParamCache")
    public CaffeineCacheManager calculateParamCache() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(
                Caffeine.newBuilder().recordStats().expireAfterWrite(300, TimeUnit.SECONDS).maximumSize(500));
        cacheManager.setCacheNames(Lists.newArrayList("calculateParamCache"));
        return cacheManager;
    }
}

直接上重点吧,啰嗦代码就不写了:

@Service
@Slf4j
public class GirlServiceImpl extends ServiceImpl<GirlMapper, Girl> implements GrilService {
    @Override
    @Cacheable(cacheNames = "calculateParamCache", cacheManager = "calculateParamCache", unless = "#result == null ", key = "#id")
    public Girl queryAll(Integer id) {
        log.info("查询id");
        return baseMapper.selectById(id);
    }
}

2.参数配置介绍

2.1 expireAfterWrite

expireAfterWrite(5, TimeUnit.SECONDS)

表示第一次查询时查询数据库,然后建立缓存,此后的5秒内,数据从缓存中获取,5秒后删除该缓存,下一次调用接口查询重复之前操作;不停进行接口请求,示例如下:
在这里插入图片描述
需要注意的时是,缓存在这5秒内是感知不到数据库的变化的,人为修改数据库数据,查询到的还是原数据;

2.2 expireAfterAccess

expireAfterAccess(5, TimeUnit.SECONDS)

和expireAfterWrite的区别就是,expireAfterWrite以第一次请求的时间为起始时间,而expireAfterAccess以最后一次的请求时间为起始时间,最后一次请求5秒后删除该缓存;示例如下:
在这里插入图片描述

2.3 refreshAfterWrite

代码示例:
核心代码

@Slf4j
@Component
public class CacheDemo {

    private final LoadingCache<Integer, Girl> tslInstanceCache;

    private final GirlServiceImpl girlService;

    public CacheDemo(GirlServiceImpl girlService) {
        this.girlService = girlService;
        this.tslInstanceCache = Caffeine.newBuilder()
                .refreshAfterWrite(5, TimeUnit.SECONDS)
                .recordStats()
                .removalListener(
                        (RemovalListener<? super Object, ? super Object>)
                                (key, value, cause) -> log.info(
                                        "cache remove, key:{}, value:{}, cause:{}",
                                        key,
                                        value,
                                        cause))
                .build(new tslCacheLoader());
    }

	//这里去查询数据库
    private class tslCacheLoader implements CacheLoader<Integer, Girl> {

        @Override
        public @Nullable Girl load(@NonNull Integer integer) throws Exception {
            log.info("start load: {}", integer);
            return girlService.query(integer);
        }

        @Override
        public @Nullable Girl reload(@NonNull Integer key, @NonNull Girl oldValue) throws Exception {
            log.info("start reload: {}", key);
            return girlService.query(key);
        }
    }
	//controller层调用此方法
    public Girl getInfo(Integer key) {
        Girl girl = tslInstanceCache.get(key);
        log.info("return:{}", JSON.toJSON(girl));
        return girl;
    }
}

运行结果介绍:
第一次调接口查询,调用load方法,这里是在主线程中进行;
在这里插入图片描述
再点一次,直接查缓存数据;
在这里插入图片描述
等一段时间控制台没有日志信息发现其实到了时间不会自动去刷新;
调用一次接口,发现调用reload接口,这个时间机制和expireAfterWrite一样,这里是用的ForkJoinPool线程池中线程;
在这里插入图片描述
补一个线程监控
在这里插入图片描述
手动修改一下数据库内容:
在这里插入图片描述
然后再去调用,发现返回的还是原来的,同时会删除原来的;
在这里插入图片描述
再请求一次,返回的才是正常数据;
在这里插入图片描述
所以使用refreshAfterWrite会有一次获取到旧值的过程!

2.4 removalListener

监听过期缓存的信息,这里使用的是com.github.benmanes.caffeine.cache的包:

    public CacheDemo(GirlServiceImpl girlService) {
        this.girlService = girlService;
        this.tslInstanceCache = Caffeine.newBuilder()
                .expireAfterWrite(5, TimeUnit.SECONDS)
                .recordStats()
                .removalListener((RemovalListener<? super Object, ? super Object>) (key, value, cause) -> {
                    log.info("cache remove, key:{}, value:{}, cause:{}", key, value, cause);
                    log.info("current remove thread:  " + Thread.currentThread().getName());
                })
                .build(new tslCacheLoader());
    }

在这里插入图片描述
发现removalListener部分其实不是在主线程中进行,额外使用了线程;

下面使用RemovalListeners.asynchronous异步处理过期缓存消息,使用的是com.google.common.cache的包,可以做一些处理,其实效果和上面差不多,可能是包的原因:

    public CacheDemo(ExecutorService executorService, GirlServiceImpl girlService) {
        this.girlService = girlService;
        this.tslInstanceCache = CacheBuilder.newBuilder()
                .expireAfterWrite(5, TimeUnit.SECONDS)
                .removalListener(RemovalListeners.asynchronous((RemovalListener<Integer, Girl>) removalNotification -> {
                    log.info("key={},value={},reason={}", removalNotification.getKey(), removalNotification.getValue(), removalNotification.getCause());
                    log.info("current remove thread:  " + Thread.currentThread().getName());
                }, executorService))
                .build(new tslCacheLoader());
    }

在这里插入图片描述
但是上述两种的load和reload都是在主线程中进行;

需要注意的是,此处用的是expireAfterWrite,如果用refreshAfterWrite的话RemovalListeners.asynchronous好像不work,可能是都是异步调用;直接用下面的方式就可以了;

    public CacheDemo(GirlServiceImpl girlService) {
        this.girlService = girlService;
        this.tslInstanceCache = Caffeine.newBuilder()
                .refreshAfterWrite(5, TimeUnit.SECONDS)
                .recordStats()
                .removalListener((RemovalListener<? super Object, ? super Object>) (key, value, cause) -> {
                    log.info("cache remove, key:{}, value:{}, cause:{}", key, value, cause);
                    log.info("current remove thread:  " + Thread.currentThread().getName());
                })
                .build(new tslCacheLoader());
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值