package com.demo.utils; import org.springframework.util.StringUtils; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * Description: 缓存工具类 * 1.部分方法未验证,如有问题请自行修改 * 2.其他方法请自行添加 * * @Author: zhx & moon hongxu_1234@163.com * @Date: 2022-04-07 20:54 * @version: V1.0.0 */ public class Cache { /** * 屏蔽工具类的无参构造 避免工具类被实例化 */ private Cache(){} /** * 缓存留存期 30min 1H 24H */ public static final long CACHE_HOLD_TIME_30M = 30 * 60 * 1000L; public static final long CACHE_HOLD_TIME_1H = 2 * CACHE_HOLD_TIME_30M; public static final long CACHE_HOLD_TIME_24H = 24 * CACHE_HOLD_TIME_1H; public static final long CACHE_HOLD_TIME_FOREVER = -1L; /** * 缓存容量、最少使用容量 */ private static final int CACHE_MAX_CAP = 1000; private static final int CLEAN_LRU_CAP = 800; /** * 缓存当前大小 */ private static AtomicInteger CACHE_CURRENT_SIZE = new AtomicInteger(0); /** * 缓存对象 */ private static final Map<String,Node> CACHE_MAP = new ConcurrentHashMap<>(CACHE_MAX_CAP); /** * 最少使用记录 */ private static final List<String> LRU_LIST = new LinkedList<>(); /** * 自动清理标志位 */ private static volatile boolean CLEAN_RUN_FLAG = false; /** * 默认30MIN * @param key * @param val */ public static void put(String key,Object val){ put(key,val,CACHE_HOLD_TIME_30M); } /** * 添加永久缓存 * @param key * @param val */ public static void putForever(String key,Object val){ put(key,val,CACHE_HOLD_TIME_FOREVER); } /** * 添加缓存 * @param key * @param val * @param ttlTime */ public static void put(String key,Object val,long ttlTime){ if (!StringUtils.hasLength(key) || null == val){ return; } checkSize(); updateCacheLru(key); CACHE_MAP.put(key,new Node(val,ttlTime)); } /** * 获取缓存信息 * @param key * @param clazz * @param <T> * @return */ public static <T> T get(String key,Class<T> clazz){ if (!StringUtils.hasLength(key) || !CACHE_MAP.containsKey(key)){ return null; } updateCacheLru(key); return (T) CACHE_MAP.get(key).getVal(); } /** * 更新最近使用位置 * @param key */ private static void updateCacheLru(String key){ synchronized (LRU_LIST){ LRU_LIST.remove(key); LRU_LIST.add(0,key); } } /** * 删除,成功则容量-1 * @param key */ private static boolean remove(String key){ Node node = CACHE_MAP.remove(key); if (null!=node){ CACHE_CURRENT_SIZE.getAndDecrement(); return true; } return false; } /** * 检查是否超过容量,先清理过期,在清理最少使用 */ private static void checkSize(){ if (CACHE_CURRENT_SIZE.intValue() > CACHE_MAX_CAP){ deleteTimeOut(); } if (CACHE_CURRENT_SIZE.intValue() > CLEAN_LRU_CAP){ deleteLru(); } } /** * 删除最久未使用,尾部删除 * 永久缓存不会被清除 */ private static void deleteLru(){ synchronized (LRU_LIST){ while (LRU_LIST.size() > CLEAN_LRU_CAP){ int lastIndex = LRU_LIST.size() - 1; String key = LRU_LIST.get(lastIndex); if (!CACHE_MAP.get(key).isForever() && remove(key)){ LRU_LIST.remove(lastIndex); } } } } /** * 删除过期 */ private static void deleteTimeOut(){ List<String> del = new LinkedList<>(); for (Map.Entry<String,Node> entry:CACHE_MAP.entrySet()){ if (entry.getValue().isExpired()){ del.add(entry.getKey()); } } for (String k:del){ remove(k); } } /** * 缓存是否已存在,过期则删除返回False * @param key * @return */ public static boolean contains(String key){ if (CACHE_MAP.containsKey(key)){ if (!CACHE_MAP.get(key).isExpired()){ return true; } if (remove(key)){ return false; } return true; } return false; } /** * 清空缓存 */ public static void clear(){ CACHE_MAP.clear(); CACHE_CURRENT_SIZE.set(0); LRU_LIST.clear(); } /** * 重置自动清理标志 * @param flag */ public static void setCleanRunFlag(boolean flag){ CLEAN_RUN_FLAG = flag; } /** * 自动清理过期缓存 */ private static void startAutoClean(){ if (!CLEAN_RUN_FLAG){ setCleanRunFlag(true); ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1); scheduledExecutor.scheduleAtFixedRate(()->{ try { Cache.setCleanRunFlag(true); while (CLEAN_RUN_FLAG){ Cache.deleteTimeOut(); } } catch (Exception e) { e.printStackTrace(); } },10,Cache.CACHE_HOLD_TIME_1H, TimeUnit.SECONDS); } } /** * 缓存对象类 */ public static class Node{ /** * 缓存值 */ private Object val; /** * 过期时间 */ private long ttlTime; public Node(Object val,long ttlTime){ this.val = val; if (ttlTime<0){ this.ttlTime = ttlTime; }else{ this.ttlTime = System.currentTimeMillis() + ttlTime; } } public Object getVal(){ return this.val; } public boolean isExpired(){ if (this.ttlTime<0){ return false; } return System.currentTimeMillis() > this.ttlTime; } public boolean isForever(){ if (this.ttlTime<0){ return true; } return false; } } }
缓存组件 | 类型 |
---|---|
HAZELCAST | 分布式缓存 |
INFINISPAN | 分布式缓存 |
COUCHBASE | 分布式缓存 |
REDIS | 分布式缓存 |
CAFFEINE | 本地缓存 |
CACHE2K | 本地缓存 |
Dengan pengembangan sistem perkakasan Dengan peningkatan perisian, status dan kebolehgunaan cache dalam aplikasi semakin meningkat dari hari ke hari SpringBoot telah mereka set mekanisme caching universal (spesifikasi) untuk tujuan ini
Spesifikasi ini mereka bentuk dua antara muka peringkat teratas Cache dan CacheManager, iaitu pengurusan cache dan cache. Dengan melaksanakan CacheManager dan memperkenalkan komponen cache, anda boleh menyediakan cache dengan mudah melalui anotasi dalam projek SpringBoot
Gunakan kelas konfigurasi automatik cache SpringBoot untuk melihat komponen cache yang boleh disokongnya kod sumber adalah seperti berikut:
//org.springframework.boot.autoconfigure.cache.CacheConfigurations static { Map<CacheType, String> mappings = new EnumMap<>(CacheType.class); mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class.getName()); mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class.getName()); mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class.getName()); mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class.getName()); mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName()); mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName()); mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName()); mappings.put(CacheType.CACHE2K, Cache2kCacheConfiguration.class.getName()); mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName()); mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName()); MAPPINGS = Collections.unmodifiableMap(mappings); }
注解 | 功能 |
---|---|
@EenableCacheing | 启用注解式缓存的功能,一般加在项目启动类上 |
@Cacheable | 如果存在缓存,则返回缓存信息;不存在则获取值并添加到缓存 |
@CachePut | 添加缓存,可用于更新方法(强制将方法返回值添加到指定Key) |
@CacheEvict | 删除缓存 |
@Caching | 打包操作,将上面几种注解打包在一起作用 |
@CacheConfig | 通用配置注解,如果要对某个对象设置缓存,可以将此注解标注在类上设置缓存名、主键生成器等 |
Sepadukan Kafein melalui SpringBoot untuk demonstrasi anotasi cache maklumat versi, rujuk kepada dependencies
<?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>LenovoTest</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>19</maven.compiler.source> <maven.compiler.target>19</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>3.0.0</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring.version}</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.14.graal</version> </dependency> </dependencies> </project>
server: port: 8088 spring: cache: type: caffeine custom-caffeine: specs: ## 用户信息写入10S后过期 userInfo: maximumSize=10,expireAfterWrite=10s ## 登陆信息写入5S后过期 accessInfo: maximumSize=10,expireAfterWrite=5s
package com.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; /** * @author * @date * @since 1.8 */ @EnableCaching @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class,args); } }
Jika jenis pelaksanaan cache tidak dinyatakan melalui Yml, pelaksanaan lalai akan digunakan
package com.demo.comfig; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * @author * @date * @since 1.8 */ @Configuration public class CustomCaffeineConfig { /** * 加载 Caffeine 配置 * @return */ @Bean(name = "caffeineProperties") @ConfigurationProperties(prefix = "custom-caffeine.specs") public Map<String,String> caffeineProperties(){ return new HashMap(16); } /** * 自定义 CacheManager * @param properties * @return */ @Bean @Primary public CacheManager caffeineManager(@Qualifier("caffeineProperties") Map<String,String> properties){ CaffeineCacheManager manager = new CaffeineCacheManager(); Map.Entry<String,String> entry; Iterator<Map.Entry<String,String>> iterator = properties.entrySet().iterator(); while (iterator.hasNext()){ entry = iterator.next(); manager.registerCustomCache(entry.getKey(), Caffeine.from(entry.getValue()).build()); } return manager; } }
package com.demo.entity; import lombok.Data; /** * @author zhanghx19 * @date 2023-01-28 15:53 * @since 1.8 */ @Data public class UserInfo { private String name; private String account; private long age; }
Tentukan kelas Pengawal untuk Ujian
package com.demo.controller; import com.demo.entity.UserInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author * @date 2023-01-28 11:36 * @since 1.8 */ @RestController @RequestMapping("/test") public class TestController { /** * 注入缓存管理器,所有注解操作也可以直接操作管理器实现 */ @Autowired CacheManager cacheManager; /** * CachePut 强制刷新缓存 * @param id * @param val * @return */ @GetMapping("/update") @CachePut(cacheNames = "test" ,key = "#id") public String update(String id,String val){ //TODO Query Data By @{id} return val; } /** * Cacheable 查看缓存,存在则直接返回;否则查询数据,添加缓存并返回 * @param id * @param val * @return */ @GetMapping("/query") @Cacheable(cacheNames = "test" ,key = "#id" ) public String query(String id,String val){ //TODO Query Data By @{id} return val; } /** * 删除注解内指定的 缓存名下的 Key * @param id */ @GetMapping("/deleteTest") @CacheEvict(cacheNames = "test",key = "#id") public void deleteTest(String id){ } /** * 通过 cacheManager 删除缓存 * @param cacheNames * @param id */ @GetMapping("/deleteByNameAndKet") public void deleteByNameAndKet(String cacheNames,String id){ Cache cache = cacheManager.getCache(cacheNames); cache.evict(id); } /** * CachePut 强制缓存用户信息 且10秒后过期 * @param id * @param val * @return */ @GetMapping("/updateUser") @CachePut(cacheNames = "userInfo" ,key = "#id") public String updateUser(String id,String val){ return val; } /** * Cacheable 10秒内缓存不更新,丢失后可刷新为当前值 * @param id * @param val * @return */ @GetMapping("/queryUser") @Cacheable(cacheNames = "userInfo" ,key = "#id") public String queryUser(String id,String val){ return val; } /** * 缓存对象 * @param id * @param val * @return */ @GetMapping("/queryUserById") @Cacheable(cacheNames = "userInfo" ,key = "#id") public UserInfo getUserInfo(String id,String val){ UserInfo info = new UserInfo(); info.setAccount(id); info.setName(val); info.setAge(System.currentTimeMillis()); return info; } }
6 Rekod ujian
Gunakan Cacheable untuk cuba menyegarkan cache (kembali nilai sedia ada)
Padam cache
Gunakan Boleh dicache sekali lagi untuk cuba memuat semula cache (anda boleh menyegarkannya selepas mengosongkannya di atas)
Ujian tamat tempoh automatik, tambah maklumat pengguna melalui CachePut
Cuba muat semula cache dengan Cacheable, ia akan berkuat kuasa selepas 10S
Selepas 10 saat
Maklumat objek cache
Atas ialah kandungan terperinci Cara menggunakan kelas alat caching bersepadu CacheManager di bawah SpringBoot. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!