spring boot 集成 jetcache【基础篇:@Cached、@CreateCache、@CacheRefresh】

手打不易,如果转摘,请注明出处!

注明原文:https://zhangxiaofan.blog.csdn.net/article/details/129832925


目录

前言

版本

 配置通用说明

项目结构

代码

启动类

实体类

基础使用——增删改查(@Cached、@CacheInvalidate、@CacheUpdate)

基础使用——@CreateCache注解和手动方式

基础使用——@CacheRefresh 详解


前言

最近有个项目用到jetcache,正好用到,加上spring-boot在使用jetcache的时候会有一些需要注意的坑,下面通过基础使用来给大家简单介绍。

下面有一些示例代码,都有注释,这些注释可以仔细阅读一下!

版本

本篇的jetcache、jedis等版本如下:

<dependency><groupId>com.alicp.jetcache</groupId><artifactId>jetcache-starter-redis</artifactId><version>2.7.3</version>
</dependency>
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.1.0</version>
</dependency>

完整的maven的pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>spring-boot-redis-jetcache-base</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>spring-boot-redis-jetcache-base</name><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.6</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><exclusions><exclusion><artifactId>aspectjweaver</artifactId><groupId>org.aspectj</groupId></exclusion></exclusions></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.10</version></dependency><dependency><groupId>com.alicp.jetcache</groupId><artifactId>jetcache-starter-redis</artifactId><version>2.7.3</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.1.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.25</version></dependency><dependency><groupId>com.esotericsoftware</groupId><artifactId>kryo</artifactId><version>5.4.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.1.0.RELEASE</version></plugin></plugins></build>
</project>

配置文件 application.yml 如下: 

server:servlet:context-path: /port: 8081# 官方表示,不会支持 redisson 客户端, 仅支持 jedis 和 lettuce 链接:https://github.com/alibaba/jetcache/issues/634
jetcache:statIntervalMinutes: 10 # 默认值0,统计间隔,0表示不统计areaInCacheName: false # 默认值false, 是否将 areaName 作为远程缓存key前缀# 本地local:# 默认分组配置,可以创建多个,对应@Cached和@CreateCache的 area 属性, 默认名就是 'default'default:type: caffeine # 可选 linkedhashmap,caffeinekeyConvertor: fastjson # 指定KEY的转换方式, 可选 fastjson2,fastjson,jackson# 远程remote:default:type: rediskeyConvertor: fastjson # 指定KEY的转换方式, 可选 fastjson2,fastjson,jacksonvalueEncoder: java # 可选 java,kryo,kryo5valueDecoder: java # 可选 java,kryo,kryo5poolConfig:minIdle: 5maxIdle: 20maxTotal: 50host: 127.0.0.1port: 6379password: 123456

 配置通用说明

参考官网:https://github.com/alibaba/jetcache/blob/master/docs/CN/Config.md

属性默认值说明
jetcache.statIntervalMinutes0统计间隔,0表示不统计
jetcache.areaInCacheNametrue(2.6-) false(2.7+)jetcache-anno把cacheName作为远程缓存key前缀,2.4.3以前的版本总是把areaName加在cacheName中,因此areaName也出现在key前缀中。2.4.4以后可以配置,为了保持远程key兼容默认值为true,但是新项目的话false更合理些,2.7默认值已改为false。
jetcache.hiddenPackages@Cached和@CreateCache自动生成name的时候,为了不让name太长,hiddenPackages指定的包名前缀被截掉
jetcache.[local/remote].${area}.type缓存类型。tair、redis为当前支持的远程缓存;linkedhashmap、caffeine为当前支持的本地缓存类型
jetcache.[local/remote].${area}.keyConvertorfastjson2key转换器的全局配置,2.6.5+已经支持的keyConvertor:fastjson2/jackson
2.6.5-只有一个已经实现的keyConvertor:fastjson。仅当使用@CreateCache且缓存类型为LOCAL时可以指定为none,此时通过equals方法来识别key。方法缓存必须指定keyConvertor
jetcache.[local/remote].${area}.valueEncoderjava序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java/kryo/kryo5;2.6-可选java/kryo
jetcache.[local/remote].${area}.valueDecoderjava序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java/kryo/kryo5;2.6-可选java/kryo
jetcache.[local/remote].${area}.limit100每个缓存实例的最大元素的全局配置,仅local类型的缓存需要指定。注意是每个缓存实例的限制,而不是全部,比如这里指定100,然后用@CreateCache创建了两个缓存实例(并且注解上没有设置localLimit属性),那么每个缓存实例的限制都是100
jetcache.[local/remote].${area}.expireAfterWriteInMillis无穷大以毫秒为单位指定超时时间的全局配置(以前为defaultExpireInMillis)
jetcache.remote.${area}.broadcastChanneljetcahe2.7的两级缓存支持更新以后失效其他JVM中的local cache,但多个服务共用redis同一个channel可能会造成广播风暴,需要在这里指定channel,你可以决定多个不同的服务是否共用同一个channel。如果没有指定则不开启。
jetcache.local.${area}.expireAfterAccessInMillis0需要jetcache2.2以上,以毫秒为单位,指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持。0表示不使用这个功能。

项目结构

代码

启动类

import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
// 开启 Cache
@EnableMethodCache(basePackages = "com.myjetcache")
// 如果不用@CreateCache注解可以删除 EnableCreateCacheAnnotation
@EnableCreateCacheAnnotation
@EnableScheduling
public class SpringJetCacheApplication {public static void main(String[] args) {SpringApplication.run(SpringJetCacheApplication.class, args);}
}

实体类

@Data
public class Student implements Serializable {private static final long serialVersionUID = 1L;private static final Random RANDOM = new Random();/*** 当对象只包含基本数据类型的时候,RADM工具可以直接展示数据. 非基本数据类型的字段, 默认不会展示.*/private Integer id;private String name;private Integer age;public static Student getStudent(String id) {Student student = new Student();student.setId(Integer.parseInt(id));student.setName(UUID.randomUUID().toString().substring(0, 3));student.setAge(RANDOM.nextInt(9999));return student;}
}

基础使用——增删改查(@Cached、@CacheInvalidate、@CacheUpdate)

先定义接口

public interface JetCacheBaseService {Student add(Student student);void delete(Long id);void update(Student student);Student get(Long id);Student get(Long id, boolean isUseCache);
}

写好实现类

需注意的点:

@Cached——[增]

注意点:

  • 由于我们是在方法上使用,该注解缓存的是方法 return 的数据,因此方法返回类型不能是 void
  • 缓存执行的是 com.alicp.jetcache.Cache#PUT() , 接口默认是异步存储 

@CacheInvalidate——[删]

  • 注意点:方法执行后,再删除

@CacheUpdate——[改]

  • 注意点:方法执行后,再修改,作者说明: https://github.com/alibaba/jetcache/issues/115

@Cached——[查]

注意点:

  • 先查缓存,无缓存则走方法
  • 如果注解配合 condition 属性,那么 condition=true: 查缓存, 无缓存则走方法;condition=false: 不查缓存, 直接执行方法(查数据库)
@Slf4j
@Service
public class JetCacheBaseServiceImpl implements JetCacheBaseService {private final Random random = new Random();/*** 增 @Cached* 缓存执行的是 com.alicp.jetcache.Cache#PUT() , 接口默认是异步存储** @param student spel表达式取值* @return 待缓存的的数据(注意,这个返回值不能void)*/@Override@Cached(area = "default", name = "my:jetcache:", key = "#student.id", cacheType = CacheType.BOTH,expire = 3600, localExpire = 10, timeUnit = TimeUnit.SECONDS, cacheNullValue = false)public Student add(Student student) {log.info("student:{}", JSON.toJSONString(student));return student;}/*** 删 @CacheInvalidate* 方法执行后,再删除,作者 huangli 表示也不会增加这个功能选项, 自主控制粒度即可*/@Override@CacheInvalidate(name = "my:jetcache:", key = "#id")public void delete(Long id) {log.info("delete");}/*** 改 @CacheUpdate* 方法执行后,再修改. 作者说明:https://github.com/alibaba/jetcache/issues/115*/@Override@CacheUpdate(name = "my:jetcache:", key = "#student.id", value = "#student")public void update(Student student) {log.info("update:{}", JSON.toJSONString(student));}/*** 查* 先查缓存,无缓存则走方法*/@Override@Cached(name = "my:jetcache:", key = "#id")public Student get(Long id) {log.info("load from db");// 当缓存不存在, 模拟从数据库查询return loadFromDb(id);}/*** 查* condition=true: 查缓存, 无缓存则走方法* condition=false: 不查缓存, 直接执行方法(查数据库)*/@Override@Cached(name = "my:jetcache:", key = "#id", condition = "#isUseCache==true")public Student get(Long id, boolean isUseCache) {// 当 isUseCache 为 false, 或者 缓存不存在的时候, 插叙数据库log.info("load from db, isUseCache:{}", isUseCache);// 当缓存不存在, 模拟从数据库查询return loadFromDb(id);}/*** 模拟从数据库查询*/private Student loadFromDb(Long id) {Student student = new Student();student.setId(Math.toIntExact(id));student.setName("load from db");student.setAge(random.nextInt(100));return student;}
}

基础使用——@CreateCache注解和手动方式

由于 @CreateCache 以及标记为@Deprecated了,这里优先将手动方式

我们先看下属性说明:

@CreateCache属性表

参考官网Link:https://github.com/alibaba/jetcache/blob/master/docs/CN/CreateCache.md#createcache%E5%B1%9E%E6%80%A7%E8%A1%A8

属性默认值说明
area“default”如果需要连接多个缓存系统,可在配置多个cache area,这个属性指定要使用的那个area的name
name未定义指定缓存的名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。如果两个@CreateCachenamearea相同,它们会指向同一个Cache实例
expire未定义该Cache实例的默认超时时间定义,注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取无穷大
timeUnitTimeUnit.SECONDS指定expire的单位
cacheTypeCacheType.REMOTE缓存的类型,包括CacheType.REMOTE、CacheType.LOCAL、CacheType.BOTH。如果定义为BOTH,会使用LOCAL和REMOTE组合成两级缓存
localLimit未定义如果cacheType为CacheType.LOCAL或CacheType.BOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取100
serialPolicy未定义如果cacheType为CacheType.REMOTE或CacheType.BOTH,指定远程缓存的序列化方式。JetCache内置的可选值为SerialPolicy.JAVA和SerialPolicy.KRYO。注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取SerialPolicy.JAVA
keyConvertor未定义指定KEY的转换方式,用于将复杂的KEY类型转换为缓存实现可以接受的类型,JetCache内置的可选值为KeyConvertor.FASTJSON和KeyConvertor.NONE。NONE表示不转换,FASTJSON通过fastjson将复杂对象KEY转换成String。如果注解上没有定义,则使用全局配置。

先定义接口

public interface CreateCacheUseMethodService {Cache<String, String> getStringMethodCache();
}

注意实现类有下面几个要注意的地方:

  • refresh属性功能,必须要 QuickConfig 显示创建,不要用 cache.config().setRefreshPolicy() ,否则可能会出现无效的情况
  • cache.config().setLoader() 加载器会遍历每一个key,并执行这个加载器;缓存没有key的时候是不会执行的,首次 set/get 都会创建 key 
  • 由于refresh 会用加载器刷新所有key,那么一定要设置 stopRefreshAfterLastAccess,表示多久不使用对应的key缓存则会停止刷新。
  • 本地缓存的时间 < 远程缓存的时间,缓存refresh的时间可以接近远程缓存时间,也可以大于它

代码如下,可以仔细阅读注释:

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.CacheManager;
import com.alicp.jetcache.RefreshPolicy;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.template.QuickConfig;
import com.myjetcache.service.CreateCacheUseMethodService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.Duration;
import java.util.UUID;import javax.annotation.PostConstruct;@Slf4j
@Service
public class CreateCacheUseMethodServiceImpl implements CreateCacheUseMethodService {@Autowiredprivate CacheManager cacheManager;private Cache<String, String> stringMethodCache;@PostConstructpublic void init() {// 注意必须要refreshPolicy() ,才能创建 RefreshCache, 否则自动刷新功能无效QuickConfig quickConfigStringCache = QuickConfig.newBuilder("myStringCache:use:method:") // 缓存的前缀.cacheType(CacheType.BOTH) // local 和 remote 组合成两级缓存.expire(Duration.ofSeconds(3600)) // 远程过期时间.localExpire(Duration.ofSeconds(5)) // 本地过期时间, 应该小于远程过期时间, 只对CacheType.LOCAL和CacheType.BOTH有效.localLimit(1000) // 本地缓存的最大元素数量, 默认:100.cacheNullValue(false) // 是否缓存 NULL 值.refreshPolicy(getRefreshPolicy()) // 这里必须显式创建, 不要使用 cache.config().setRefreshPolicy(), 否则无效.build();stringMethodCache = cacheManager.getOrCreateCache(quickConfigStringCache);// 刷新执行的加载器, 会遍历刷新每一个keystringMethodCache.config().setLoader(this::loadFromDb);}/*** 创建刷新策略* 等于注解:@CacheRefresh(refresh = 10, refreshLockTimeout = 10, stopRefreshAfterLastAccess = 3600, timeUnit = TimeUnit.SECONDS)*/private RefreshPolicy getRefreshPolicy() {RefreshPolicy refreshPolicy = new RefreshPolicy();// 刷新时间间隔refreshPolicy.setRefreshMillis(10 * 1000L);// 类型为 BOTH/REMOTE 的缓存刷新时,同时只会有一台服务器在刷新,这台服务器会在远程缓存放置一个分布式锁,此配置指定该锁的超时时间// 不管有多少台服务器,同时只有一个服务器在刷新,这是通过 tryLock 实现的refreshPolicy.setRefreshLockTimeoutMillis(10 * 1000);// 指定多久未访问后停止自动刷新。 注意:不指定则会一直刷新refreshPolicy.setStopRefreshAfterLastAccessMillis(3600 * 1000);return refreshPolicy;}/*** 刷新执行的加载器* 每个key都有一个刷新任务, 因此必须设置 stopRefreshAfterLastAccess* 注意: 没有key则不会定时执行数据库加载器, 首次 get/set 都相当于创建了key** @param key 刷新的key*/public String loadFromDb(String key) {// 模拟从数据库读取数据String uuid = UUID.randomUUID().toString();log.info("[Use Method] key:{},load cache form db:{}", key, uuid);return uuid;}@Overridepublic Cache<String, String> getStringMethodCache() {return stringMethodCache;}
}

 上面是手动创建,接下来我们看下如何使用注解 @CreateCache 创建(不建议使用该注解,因为被作者标记为过期 @Deprecated 了)

先定义一个接口

public interface CreateCacheUseAnnotationService {Cache<String, String> getStringCache();
}

实现类如下,注解的属性跟上面手动方式的属性可以对应,可以看上面代码的注解。

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheRefresh;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import com.myjetcache.service.CreateCacheUseAnnotationService;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.util.UUID;
import java.util.concurrent.TimeUnit;import javax.annotation.PostConstruct;@Slf4j
@Service
public class CreateCacheUseAnnotationServiceImpl implements CreateCacheUseAnnotationService {/*** 注解方式* local 失效后, 会从 redis 查, redis 也没有则会执行数据库加载器*/@CreateCache(name = "myStringCache:use:annotation:", cacheType = CacheType.BOTH, expire = 3600, localExpire = 5,localLimit = 1000)@CacheRefresh(refresh = 20, refreshLockTimeout = 10, stopRefreshAfterLastAccess = 3600, timeUnit = TimeUnit.SECONDS)private Cache<String, String> stringCache;@PostConstructpublic void init() {// 1.cache.get(),如果local和redis都没有缓存, 则会执行数据库加载器 loadFromDb()// 2.定时刷新刷新的加载器, 会为每一个key创建刷新任务; 注意: 没有key则不会定时执行数据库加载器, 首次 get/set 都相当于创建了keystringCache.config().setLoader(this::loadFromDb);}/*** 刷新执行的加载器,jetcache会为每个key都会创建一个刷新任务, 因此必须设置 stopRefreshAfterLastAccess, 否则内存消耗太大** @param key 刷新的key*/public String loadFromDb(String key) {// 模拟从数据库读取数据String uuid = UUID.randomUUID().toString();log.info("[Use Annotation] key:{},load cache form db:{}", key, uuid);return uuid;}@Overridepublic Cache<String, String> getStringCache() {return stringCache;}
}

基础使用——@CacheRefresh 详解

需要注意的点如下:

注意 CacheRefresh 会为每个key创建定时任务, 定时来执行这个方法
官方文档:
1.目的是为了防止缓存失效时造成的雪崩效应打爆数据库
2.对key比较少,实时性要求不高,加载开销非常大的缓存场景,适合使用自动刷新

CacheRefresh 刷新机制:
1.如果 CacheType.LOCAL ,那么多个节点会重复刷新。
2.如果 CacheType.REMOTE ,通过在远程缓存中的分布式锁'_#TS#',保证一个周期内只有一个节点执行了刷新操作.
3.如果 CacheType.BOTH ,即两级缓存,通过在远程缓存中的分布式锁,保证一个周期内只有一个节点执行了刷新操作.

注意:仅更新REMOTE,其节点的本地缓存不会更新.jetcache支持给远程和本地缓存设置不同的超时时间,所以可以把本地缓存的超时时间设置短一点.

CachePenetrationProtect 注解作用——当缓存访问【未命中】的情况下,对并发进行的加载行为进行保护;当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果

具体代码如下:

public interface RefreshService {String getRefreshStringCache(String key);Student getRefreshMapCache(Object obj);
}

实现类: 


import com.alibaba.fastjson.JSON;
import com.alicp.jetcache.anno.CachePenetrationProtect;
import com.alicp.jetcache.anno.CacheRefresh;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.myjetcache.entity.Student;
import com.myjetcache.service.RefreshService;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;@Slf4j
@Service
public class RefreshServiceImpl implements RefreshService {private final Random random = new Random();private final AtomicInteger atomicInteger = new AtomicInteger(0);/*** 注意 CacheRefresh 会为每个key创建定时任务, 定时来执行这个方法* 官方文档:* 1.目的是为了防止缓存失效时造成的雪崩效应打爆数据库* 2.对key比较少,实时性要求不高,加载开销非常大的缓存场景,适合使用自动刷新* <p>* CacheRefresh 刷新机制:* 1.如果 CacheType.LOCAL ,那么多个节点会重复刷新。* 2.如果 CacheType.REMOTE ,通过在远程缓存中的分布式锁'_#TS#',保证一个周期内只有一个节点执行了刷新操作.* 3.如果 CacheType.BOTH ,即两级缓存,通过在远程缓存中的分布式锁,保证一个周期内只有一个节点执行了刷新操作.* 注意:仅更新REMOTE,其节点的本地缓存不会更新.jetcache支持给远程和本地缓存设置不同的超时时间,所以可以把本地缓存的超时时间设置短一点.* <p>* CachePenetrationProtect 注解:* 当缓存访问【未命中】的情况下,对并发进行的加载行为进行保护.* 当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果** @param key 缓存的 key* @return 缓存的 value*/@Override@Cached(name = "my:refresh:stringCache.", key = "#key", cacheNullValue = true, cacheType = CacheType.BOTH,expire = 3600, localExpire = 60, timeUnit = TimeUnit.SECONDS)@CacheRefresh(timeUnit = TimeUnit.SECONDS, refresh = 10, refreshLockTimeout = 10, stopRefreshAfterLastAccess = 3600)@CachePenetrationProtectpublic String getRefreshStringCache(String key) {String value;// 模拟查询数据库, 首次查询返回 null, 以后每次查询,如果缓存不存在,则返回 随机uuidif (atomicInteger.get() == 0) {atomicInteger.addAndGet(1);value = null;} else {// 每隔10秒就会刷新新的uuid到 本地和redisvalue = UUID.randomUUID().toString();}log.info("return value:{}", value);return value;}/*** Map结构的SpEL表达式参考下面的写法* 入参是的 SpEL是 key* return 是存储的 value*/@Override@Cached(area = "default", name = "my:jetcache:", key = "#obj['id']", cacheType = CacheType.BOTH,expire = 3600, localExpire = 60, timeUnit = TimeUnit.SECONDS, cacheNullValue = false)@CacheRefresh(timeUnit = TimeUnit.SECONDS, refresh = 10, refreshLockTimeout = 10, stopRefreshAfterLastAccess = 3600)@CachePenetrationProtectpublic Student getRefreshMapCache(Object obj) {log.info("obj:{}", JSON.toJSONString(obj));Student student = Student.getStudent(String.valueOf(random.nextInt(100)));log.info("after refresh:{}", JSON.toJSONString(student));return student;}
}

测试类:

@RestController
@Slf4j
public class RefreshController {private final Random random = new Random();@Autowiredprivate RefreshService refreshService;@GetMapping("/refreshstringcache/get")public String getStringCache() {String stringCache = refreshService.getRefreshStringCache("myStringCache");log.info(stringCache);return stringCache;}@GetMapping("/getrefreshmapcache/get")public Student getRefreshMapCache() {Map<String, Object> body = new HashMap<>();body.put("userId", "1");body.put("id", "123");return refreshService.getRefreshMapCache(body);}
}

上面代码是Jetcache的简单自测,满足一般的业务开发,如果更深入的了解,请到官网学习:https://github.com/alibaba/jetcache/blob/master/docs/CN/Readme.md

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/35258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

opencv实战项目 手势识别-手势控制键盘

手势识别是一种人机交互技术&#xff0c;通过识别人的手势动作&#xff0c;从而实现对计算机、智能手机、智能电视等设备的操作和控制。 1. opencv实现手部追踪&#xff08;定位手部关键点&#xff09; 2.opencv实战项目 实现手势跟踪并返回位置信息&#xff08;封装调用&am…

(原创)Flutter与Native页面互相跳转

前言 实际开发混合项目时&#xff0c;常常会有页面跳转的需求 如果是原生界面和flutter界面需要互相跳转 这种情况应该怎么处理呢&#xff1f; 今天这篇博客主要就来介绍下这个情况 其实想一下&#xff0c;这个问题可以拆成四个小的问题来分析&#xff1a; 1&#xff1a;原生界…

什么是全局代理,手机怎么设置全局代理

目录 什么是全局代理 全局代理的优缺点 优点 缺点 手机怎么设置全局代理 注意事项 总结 在计算机网络和信息安全中&#xff0c;全局代理是一种常用的技术手段&#xff0c;用于将网络流量通过代理服务器进行转发和处理。本文将介绍什么是全局代理&#xff0c;探讨全局代理…

pyspark笔记 pyspark.sql.functions

col qqpyspark 笔记 pyspark.sql.function col VS select_UQI-LIUWJ的博客-CSDN博客 取某一列 lit 创建一个包含指定值的列 date_trunc 将日期截取成由第一个参数指定的字符串值 year, yyyy, yy——截取到年month,mon,mm——截取到月day,dd ——截取到天microsecondmillis…

Highcharts引入

Highcharts是和jQuery一起使用的&#xff0c;所以需要下载好jQuery jQuery下载方式&#xff1a;访问&#xff1a;http://cdn.staticfile.org/jquery/2.1.4/jquery.min.js&#xff0c;然后全选复制到自己新建的txt文档中&#xff0c;最后把扩展名改为js。 Highcharts下载方式&…

pytest运行时参数说明,pytest详解,pytest.ini详解

一、Pytest简介 1.pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要有一下几个特点&#xff1a; 简单灵活&#xff0c;容易上手&#xff0c;支持参数化 2.能够支持简单的单元测试和复杂的功能测试&#xff0c;还可以用来做selenium、appium等自动化测试&#xf…

使用sqlplus连接oracle,提示ORA-01034和ORA-27101

具体内容如下 PL/SQL Developer 处 登录时 终端处 登录时 ERROR: ORA-01034: ORACLE not available ORA-27101: shared memory realm does not exist Process ID: 0 Session ID: 0 Serial number: 0 解决方法是执行以下命令 sqlplus /nolog conn / as sysdba startup …

【Hilog】鸿蒙系统日志源码分析

【Hilog】鸿蒙系统日志源码分析 Hilog采用C/S结构&#xff0c;Hilogd作为服务端提供日志功能。Client端通过API调用&#xff08;最终通过socket通讯&#xff09;与HiLogd打交道。简易Block图如下。 这里主要分析一下。Hilog的读、写、压缩落盘&#xff0c;以及higlog与android…

学术论文GPT源码解读:从chatpaper、chatwithpaper到gpt_academic

前言 之前7月中旬&#xff0c;我曾在微博上说准备做“20个LLM大型项目的源码解读” 针对这个事&#xff0c;目前的最新情况是 已经做了的&#xff1a;LLaMA、Alpaca、ChatGLM-6B、deepspeedchat、transformer、langchain、langchain-chatglm知识库准备做的&#xff1a;chatpa…

chapter 1 formation of crystal, basic concepts

chapter 1 晶体的形成 1.1 Quantum Mechanics and atomic structure 1.1.1 Old Quantum Theory problems of planetary model: atom would be unstableradiate EM wave of continuous frequency to solve the prablom of planetary model: Bohr: Quantum atomic structureP…

yolov5、YOLOv7、YOLOv8改进:注意力机制CA

论文题目&#xff1a;《Coordinate Attention for Efficient Mobile NetWork Design》论文地址&#xff1a; https://arxiv.org/pdf/2103.02907.pdf 本文中&#xff0c;作者通过将位置信息嵌入到通道注意力中提出了一种新颖的移动网络注意力机制&#xff0c;将其称为“Coordin…

拓扑布局和建立小型网络

练习 2.6.1&#xff1a;拓扑布局和建立小型网络 地址表 本实验不包括地址表。 拓扑图 学习目标 正确识别网络中使用的电缆物理连接点对点交换网络验证每个网络的基本连通性 简介&#xff1a; 许多网络问题都可以在网络的物理层解决。因此&#xff0c;必须清楚了解网络连接…

Python数据分析实战-列表字符串、字符串列表、字符串的转化(附源码和实现效果)

实现功能 str([None,master,hh]) ---> [None,"master","hh"] ---> "None,master,hh" 实现代码 import re import astx1 str([None,master,hh]) print(x1)x2 ast.literal_eval(x1) print(x2)x3 ",".join(str(item) for item…

阿里云服务器是什么?阿里云服务器有什么优缺点?

阿里云服务器是什么&#xff1f;云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;云服务器可以降低IT成本提升运维效率&#xff0c;免去企业或个人前期采购IT硬件的成本&#xff0c;阿里云服务器让用户像使用水、电、天然气等公共资源一样便捷、高效地使用服务器…

Controller是线程安全吗?如何实现线程安全

测试是否是线程安全 RequestMapping("/test") RestController public class TestController {//1、定义num&#xff0c;判断不同线程访问的时候&#xff0c;num的返回结果是否一致private Integer num0;/*** 2、定义两个方法*/GetMapping("/count1")publi…

【UE4 RTS】08-Setting up Game Clock

前言 本篇实现的效果是在游戏运行后能够记录当前的游戏时间&#xff08;年月日时分秒&#xff09;&#xff0c;并且可以通过修改变量从而改变游戏时间进行的快慢。 效果 步骤 1. 在Blueprints文件夹中新建如下两个文件夹&#xff0c;分别命名为“GameSettings”、“Player”…

JZ33二叉搜索树的后序遍历序列

题目地址&#xff1a;二叉搜索树的后序遍历序列_牛客题霸_牛客网 题目回顾&#xff1a; 解题思路&#xff1a; 使用栈 栈的特点是&#xff1a;先进后出。 通读题目后&#xff0c;我们可以得出&#xff0c;二叉搜索树是左子节点小于根节点&#xff0c;右子节点大于根节点。 …

章节5:脚本注入网页-XSS

章节5&#xff1a;脚本注入网页-XSS XSS &#xff1a;Cross Site Script 恶意攻击者利用web页面的漏洞&#xff0c;插入一些恶意代码&#xff0c;当用户访问页面的时候&#xff0c;代码就会执行&#xff0c;这个时候就达到了攻击的目的。 JavaScript、Java、VBScript、Activ…

Elasticsearch的一些基本概念

文章目录 基本概念&#xff1a;文档和索引JSON文档元数据索引REST API 节点和集群节点Master eligible节点和Master节点Data Node 和 Coordinating Node其它节点 分片(Primary Shard & Replica Shard)分片的设定操作命令 基本概念&#xff1a;文档和索引 Elasticsearch是面…

SQL-每日一题【1517. 查找拥有有效邮箱的用户】

题目 表: Users 编写一个解决方案&#xff0c;以查找具有有效电子邮件的用户。 一个有效的电子邮件具有前缀名称和域&#xff0c;其中&#xff1a; 前缀 名称是一个字符串&#xff0c;可以包含字母&#xff08;大写或小写&#xff09;&#xff0c;数字&#xff0c;下划线 _ &…