Maison  >  Article  >  Java  >  Comment utiliser la classe d'outils de mise en cache intégrée CacheManager sous SpringBoot

Comment utiliser la classe d'outils de mise en cache intégrée CacheManager sous SpringBoot

王林
王林avant
2023-05-12 19:10:131347parcourir

1. Définition de classe d'outils personnalisée

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 intègre des composants de mise en cache open source

1. 🎜 🎜 ## 🎜🎜 ## 🎜🎜 # Composant de mise en cache # 🎜🎜 ## 🎜🎜 # Type # 🎜🎜 ## 🎜🎜 ## 🎜🎜 ## 🎜🎜 # Hazelcast # 🎜🎜 ## 🎜🎜 # Cache distribuée # 🎜🎜#

INFINISPANCOUCHBASE#🎜 🎜## 🎜 🎜 #Cache distribuécache distribuécache localCACHE2Kcache localAvec l'expansion des systèmes matériels et les mises à niveau logicielles, l'état et l'applicabilité du cache dans les applications s'améliorent de plus en plus. SpringBoot a conçu un mécanisme de cache universel (spécification) pour cela Grâce à la classe de configuration automatique du cache de SpringBoot, affichez quel cache. peuvent être utilisés, une partie du code source est la suivante :
//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);
}
annotation#🎜. 🎜 #@EenableCacheing
cache distribué
REDIS
CAFFEINE #🎜🎜 #
# 🎜🎜# Cette spécification conçoit deux interfaces de haut niveau. , Cache et CacheManager, sont le cache et la gestion du cache. En implémentant CacheManager et en introduisant des composants de cache, vous pouvez facilement définir le cache via des annotations dans le projet SpringBoot
2 Annotation du cache
Function
La fonction d'activation de la mise en cache des annotations est généralement ajoutée à la classe de démarrage du projet#🎜🎜 #


@Cacheable

Si le cache existe, renvoyez les informations du cache s'il n'existe pas, récupérez la valeur et ajoutez-la au cache#🎜 🎜#

#🎜 🎜#

@CachePutAjouter un cache, qui peut être utilisé pour mettre à jour les méthodes (forcer l'ajout de la valeur de retour de la méthode à la clé spécifiée) #🎜 🎜 ## 🎜🎜 ## 🎜🎜 ## 🎜🎜 # @ cacheevict # 🎜🎜 ## 🎜🎜 # delete cache # 🎜🎜 ## 🎜🎜 ## 🎜🎜 ## 🎜🎜 # @ @ caching # 🎜🎜 ## 🎜 🎜#Opération de packaging, empaquetant les annotations ci-dessus ensemble Annotation de configuration générale, si vous souhaitez définir un cache pour un objet, vous pouvez annoter cette annotation sur la classe pour définir le nom du cache, le générateur de clé primaire, etc. 3. 🎜🎜# Intégrez Caffeine via SpringBoot pour la démonstration des annotations de cache, dépendance de référence des informations de version associées
<?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
3.Classe de démarrage du projet# 🎜🎜#
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);
    }
}
4. Configuration du cache personnaliséSi le type d'implémentation du cache n'est pas spécifié via Yml, le l'implémentation par défaut sera utilisée5. Classe de test# 🎜🎜#Définir un objet utilisateur
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;
}
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;
    }

}
Démarrez le projet, ajoutez la mise en cache obligatoireUtilisez Cacheable pour essayer d'actualiser le cache (retourne la valeur existante) Mise en cache à nouveau pour essayer de rafraîchir le cache (vous pouvez le rafraîchir après l'avoir vidé ci-dessus) # 🎜🎜#Test d'expiration automatique, ajoutez des informations utilisateur via CachePut#🎜🎜 #Essayez de rafraîchir le cache avec Cacheable, puis cela fonctionnera après 10S Prendre effet
@CacheConfig
1.Dépendance Pom2.Configuration Yml (spécifiez l'implémentation du cache type)
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;
    }
}
Définir une classe de contrôleur pour les tests 6. Test d'enregistrement

# 🎜🎜#10 secondes plus tard #

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer