>  기사  >  Java  >  SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

王林
王林앞으로
2023-05-12 19:10:131347검색

1. 사용자 정의 도구 클래스 정의

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;
        }

    }


}

2. SpringBoot는 오픈 소스 캐시 구성 요소를 통합합니다

1. 오픈 소스 캐시 구성 요소

인피니스팬 COUCHBASEREDISCAFFEINECACHE2K 하드웨어 시스템 및 소프트웨어 업그레이드로 인해 애플리케이션의 캐시 상태 및 적용 가능성이 날로 증가하고 있습니다. SpringBoot는 이러한 목적을 위해 일련의 일반 캐싱 메커니즘(사양)을 설계했습니다. 이 사양은 Cache 및 CacheManager라는 두 가지 최상위 인터페이스를 설계했습니다. 즉, Cache 및 Cache 관리, CacheManager를 구현하고 캐시 구성 요소를 도입하면 SpringBoot 프로젝트의 주석을 통해 편리하게 캐시를 설정할 수 있습니다.
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);
}

2. 캐시 주석

주석 기능 @EenableCacheing 은 일반적으로 프로젝트 시작 클래스 @Cacheable에 추가되는 주석 캐시 기능을 활성화합니다. 캐시가 있으면 캐시 정보가 반환되고, 존재하지 않으면 값을 가져와서 캐시에 추가하세요@CachePut메서드를 업데이트하는 데 사용할 수 있는 캐시를 추가하세요. 지정된 키에 메서드 반환 값 추가)@CacheEvictDelete the 캐시@ CachingPackaging 작업, 위 주석을 함께 패키징하여 function@CacheConfigGeneral 구성 주석, 객체에 대한 캐시를 설정하려는 경우 클래스에 이 주석을 표시하여 캐시 이름 및 기본 키 생성 서버 등을 설정할 수 있습니다.3. 캐시 테스트(카페인)
통합합니다. 캐시 주석 시연을 위한 SpringBoot를 통한 카페인 관련 버전 정보는 dependency

1.Pom dependency

<?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>

2.Yml 구성(캐시 구현 유형 지정)
server:
  port: 8088

spring:
  cache:
   type: caffeine

custom-caffeine:
  specs:
    ## 用户信息写入10S后过期
    userInfo: maximumSize=10,expireAfterWrite=10s
    ## 登陆信息写入5S后过期
    accessInfo: maximumSize=10,expireAfterWrite=5s
3을 참조하세요. 구성
Yml을 통해 캐시 구현 유형을 지정하지 않으면 기본 구현이 사용됩니다
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);
    }
}
5. 테스트 클래스
사용자 개체 정의

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;
}
6 . 기록 테스트

프로젝트 시작 및 강제 캐싱 추가

Cacheable을 사용하여 캐시 새로고침 시도(기존 값 반환)

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

캐시 삭제

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

다시 사용 Cacheable 새로고침 시도 (위에서 삭제한 후 새로고침 가능)

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

자동 만료 테스트, CachePut을 통해 사용자 정보 추가

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

Cacheable로 캐시 새로고침을 시도하면 10S 이후부터 적용됩니다

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

10초 후

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

객체 정보 캐시

SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법

위 내용은 SpringBoot에서 통합 캐싱 도구 클래스 CacheManager를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제