guvua cache缓存过期时间可配置及其初始化


在上一篇文章 使用Google Guava Cache来做缓存中,使用了guava工具包中的缓存功能,其中缓存的过期时间是直接写在程序中的,为了统一管理,一般这些配置我们需要写在专门的配置文件中。接下来,文章将对过期时间配置化及过程中遇到的问题进行讲解。

1. 配置过期时间

这一步需要修改项目配置文件application.properties,配置如下:

# 这里设置过期时间为3
demo.expireTime=3

2. 动态加载过期时间

  • 需要在使用缓存的地方使用@Value注解,为过期时间变量赋值:
    @Value("${demo.expireTime:3}")
    private int expireTime;
  • 相应的,修改初始化缓存实例代码为:
    private LoadingCache<String, Optional<Student>> studentCache = CacheBuilder.newBuilder()
            // 动态加载过期时间
            .expireAfterAccess(expireTime, TimeUnit.SECONDS)
            .build(new CacheLoader<String, Optional<Student>>() {
                @Override
                public Optional<Student> load(String s) throws Exception {
                    return Optional.ofNullable(getStudent(s));
                }
            });

其他代码不变,正常情况下输出应该与使用Google Guava Cache来做缓存最后的输出一致,但实际输出为:

2019-10-18 15:37:56.047  INFO 13252 --- [nio-1234-exec-1] c.e.s.controller.TestController          : start test.
firstTime:
写缓存【name0】
2019-10-18 15:37:56.062  INFO 13252 --- [nio-1234-exec-1] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":1,"name":"name0","sex":0,"age":10}}
写缓存【name1】
2019-10-18 15:37:56.062  INFO 13252 --- [nio-1234-exec-1] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":2,"name":"name1","sex":0,"age":10}}
secondTime:
写缓存【name0】
2019-10-18 15:37:59.063  INFO 13252 --- [nio-1234-exec-1] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":3,"name":"name0","sex":0,"age":10}}
写缓存【name1】
2019-10-18 15:37:59.063  INFO 13252 --- [nio-1234-exec-1] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":4,"name":"name1","sex":0,"age":10}}
thirdTime:
写缓存【name0】
2019-10-18 15:38:01.063  INFO 13252 --- [nio-1234-exec-1] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":5,"name":"name0","sex":0,"age":10}}
写缓存【name1】
2019-10-18 15:38:01.063  INFO 13252 --- [nio-1234-exec-1] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":6,"name":"name1","sex":0,"age":10}}
2019-10-18 15:38:01.063  INFO 13252 --- [nio-1234-exec-1] c.e.springclouddemo.manager.TestManager  : expireTime:3
2019-10-18 15:38:01.063  INFO 13252 --- [nio-1234-exec-1] c.e.s.controller.TestController          : end test.

从结果可以看出,缓存时间expireTime = 3,但studentCache没有起到我们预想的缓存作用,每次从缓存中取数时都会去新增数据,这是为什么呢?

通过断点发现studentCache在初始化时,expireTime的值为0,即@Value注解没有起到作用。
expireTime初始化失败

3. 使用@PostConstruct注解

@PostConstruct注解用于在完成依赖项注入后执行的方法,以执行任何初始化。注意相关说明表示:

  • 该注解被用来修饰一个非静态的void()方法
  • 该注解可以用来修饰一个final方法
  • 如果该方法抛出未检查的异常,则该类不能放在服务中,除非在EJB的情况下,EJB可以处理异常甚至从异常中恢复

@PostConstruct注解的方法在整个Bean初始化中的执行顺序(看网友的帖子Spring @PostConstruct 、@Autowired和Construct 顺序可以有比较直观的感受):

Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

使用@PostConstruct注解后的代码为:

    private LoadingCache<String, Optional<Student>> studentCache;

    @PostConstruct
    private void initCache() {
        studentCache = CacheBuilder.newBuilder()
                .expireAfterAccess(expireTime, TimeUnit.SECONDS)
                .build(new CacheLoader<String, Optional<Student>>() {
                    @Override
                    public Optional<Student> load(String s) throws Exception {
                        return Optional.ofNullable(getStudent(s));
                    }
                });
    }

此时再重启项目,可以发现过期时间expireTime的值发生了变化:使用@PostConstruct注解后,expireTime值加载成功
通过测试,此时缓存才真正发挥作用,控制台的输出为:

2019-10-18 16:09:46.006  INFO 23116 --- [nio-1234-exec-2] c.e.s.controller.TestController          : start test.
firstTime:
写缓存【name0】
2019-10-18 16:09:46.018  INFO 23116 --- [nio-1234-exec-2] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":1,"name":"name0","sex":0,"age":10}}
写缓存【name1】
2019-10-18 16:09:46.018  INFO 23116 --- [nio-1234-exec-2] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":2,"name":"name1","sex":0,"age":10}}
secondTime:
写缓存【name0】
2019-10-18 16:09:49.023  INFO 23116 --- [nio-1234-exec-2] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":3,"name":"name0","sex":0,"age":10}}
写缓存【name1】
2019-10-18 16:09:49.023  INFO 23116 --- [nio-1234-exec-2] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":4,"name":"name1","sex":0,"age":10}}
thirdTime:
2019-10-18 16:09:51.024  INFO 23116 --- [nio-1234-exec-2] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":3,"name":"name0","sex":0,"age":10}}
2019-10-18 16:09:51.024  INFO 23116 --- [nio-1234-exec-2] c.e.springclouddemo.manager.TestManager  : {"value":{"studentId":4,"name":"name1","sex":0,"age":10}}
2019-10-18 16:09:51.024  INFO 23116 --- [nio-1234-exec-2] c.e.springclouddemo.manager.TestManager  : expireTime:3
2019-10-18 16:09:51.024  INFO 23116 --- [nio-1234-exec-2] c.e.s.controller.TestController          : end test.

4. 测试代码

测试类的完整代码如下:

package com.example.springclouddemo.manager;

import com.example.springclouddemo.entities.Student;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class TestManager {
    @Value("${demo.expireTime:3}")
    private int expireTime;

    private LoadingCache<String, Optional<Student>> studentCache
    @PostConstruct
    private void initCache() {
        studentCache = CacheBuilder.newBuilder()
                .expireAfterAccess(expireTime, TimeUnit.SECONDS)
                .build(new CacheLoader<String, Optional<Student>>() {
                    @Override
                    public Optional<Student> load(String s) throws Exception {
                        return Optional.ofNullable(getStudent(s));
                    }
                });
    }

    private long stuNo = 1L;
    private Gson gson = new Gson();
    private Student getStudent(String str) {
        Student st = Student.builder()
                .name(str)
                .sex(0)
                .age(10)
                .studentId(stuNo++)
                .build();
        System.out.println("写缓存【" + str +"】");
        return st;
    }


    public void test() throws ExecutionException, InterruptedException {
        System.out.println("firstTime:");
        for(int i = 0; i < 2; i++) {
            log.info(gson.toJson(studentCache.get("name" + i)));
        }

        // 休眠3秒
        Thread.sleep(3000);
        System.out.println("secondTime:");
        for(int i = 0; i < 2; i++) {
            log.info(gson.toJson(studentCache.get("name" + i)));
        }

        // 休眠2秒,少于过期时间
        Thread.sleep(2000);
        System.out.println("thirdTime:");
        for(int i = 0; i < 2; i++) {
            log.info(gson.toJson(studentCache.get("name" + i)));
        }
        log.info("expireTime:" + expireTime);
    }
}

以上是我个人的一个小实践,mark一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值