在上一篇文章 使用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注解没有起到作用。
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的值发生了变化:
通过测试,此时缓存才真正发挥作用,控制台的输出为:
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一下。