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; } } }
Cache 구성 요소 | Type |
---|---|
HAZELCAST | |
분산 캐시 | |
분산 캐시 | |
분산 캐시 | |
로컬 캐시 | |
로컬 캐시 |
SpringBoot의 캐시 자동 구성 클래스를 사용하여 소스 코드의 일부가 지원되는지 확인하세요.
//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 | |
@CacheEvict | |
@ Caching | |
@CacheConfig | |
<?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); } }
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; }
Cacheable을 사용하여 캐시 새로고침 시도(기존 값 반환)
캐시 삭제
다시 사용 Cacheable 새로고침 시도 (위에서 삭제한 후 새로고침 가능)
자동 만료 테스트, CachePut을 통해 사용자 정보 추가
Cacheable로 캐시 새로고침을 시도하면 10S 이후부터 적용됩니다
10초 후
객체 정보 캐시
위 내용은 SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!