欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCache結合Redis實現(xiàn)指定過期時間和到期自動刷新

 更新時間:2024年08月23日 10:35:23   作者:csdn_Ty  
本文主要介紹了SpringCache結合Redis實現(xiàn)指定過期時間和到期自動刷新,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

緩存作為提升應用性能的重要手段,其管理策略的合理性直接影響到應用的響應速度和數(shù)據(jù)一致性。在Spring框架中,Spring Cache提供了一種聲明式緩存的解決方案,而Redis作為高性能的緩存數(shù)據(jù)庫,被廣泛應用于緩存實現(xiàn)。本文將介紹一種通過自定義注解實現(xiàn)Spring Cache與Redis緩存過期時間管理及自動刷新的策略。

1、自定義注解CacheExpireConfig

為了更靈活地控制緩存的過期時間,我們定義了一個名為CacheExpireConfig的自定義注解。此注解支持在方法級別配置緩存的過期時間和自動刷新時間。

import java.lang.annotation.*;

/**
 * @author tangzx
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheExpireConfig {

    /**
     * 緩存過期時間,支持單位天(d)、小時(h)、分鐘(m)、秒鐘(s)(不填單位默認秒)
     * 例:2h
     */
    String expireTime() default "";

    /**
     * 緩存過期刷新時間,支持單位天(d)、小時(h)、分鐘(m)、秒鐘(s)(不填單位默認秒)
     * 例:2h
     */
    String expireRefreshTime() default "";

}

 2、使用注解

在Spring的@Cacheable注解基礎上,通過@CacheExpireConfig注解,我們可以輕松地為特定方法設置緩存過期和刷新策略。

    @Override
    @CacheExpireConfig(expireTime = "60s", expireRefreshTime = "30s")
    @Cacheable(value = "testCache", condition = "#userId != null && #userName == null ")
    public String testCache(String userId, String userName) {
        System.out.println("=====================>");
        return "success";
    }

3、啟動時加載緩存過期配置

在Spring Boot應用啟動時,通過TaRedisCacheConfigListener監(jiān)聽器,掃描所有類和方法,加載帶有@CacheExpireConfig注解的方法的緩存過期配置。

import cn.hutool.core.lang.ClassScanner;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationListener;

import java.lang.reflect.Method;
import java.util.Set;

/**
 * @author tangzx
 * @date 2022/12/17 11:05
 */
public class TaRedisCacheConfigListener implements ApplicationListener<ApplicationPreparedEvent> {

    @Override
    public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
        // 掃描所有類
        Set<Class<?>> classes = scanPackage();
        for (Class<?> target : classes) {
            Method[] methods = target.getMethods();
            for (Method method : methods) {
                // 如果方法上未同時注解@Cacheable和@CacheExpireConfig,不需要配置
                if (!method.isAnnotationPresent(Cacheable.class) || !method.isAnnotationPresent(CacheExpireConfig.class)) {
                    continue;
                }
                Cacheable cacheable = method.getAnnotation(Cacheable.class);
                CacheExpireConfig cacheExpireConfig = method.getAnnotation(CacheExpireConfig.class);
                String expireTime = cacheExpireConfig.expireTime();
                String expireRefreshTime = cacheExpireConfig.expireRefreshTime();
                String[] cacheNames = ArrayUtils.addAll(cacheable.cacheNames(), cacheable.value());
                boolean autoRefresh = cacheExpireConfig.autoRefresh();
                for (String cacheName : cacheNames) {
                    MethodCacheExpireConfig methodCacheExpireConfig = MethodCacheExpireConfig.builder()
                            .expireTime(DurationUtils.parseDuration(expireTime).getSeconds())
                            .expireRefreshTime(DurationUtils.parseDuration(expireRefreshTime).getSeconds())
                            .autoRefresh(autoRefresh)
                            .target(target)
                            .method(method)
                            .build();
                    TaRedisCacheFactory.addCacheExpireConfig(cacheName, methodCacheExpireConfig);
                }
            }
        }
    }

    private Set<Class<?>> scanPackage() {
        // 使用的hutool的類掃描器,如果項目中未使用工具類,可自行實現(xiàn)
        return ClassScanner.scanPackage();
    }

}
    public static void main(String[] args) {
        SpringApplication application = new SpringApplicationBuilder().sources(StartApplication.class).build(args);
        try {
            application.addListeners(new TaRedisCacheConfigListener());
            application.run(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

4、重寫RedisCacheManager,設置過期時間

通過重寫RedisCacheManager,我們可以根據(jù)配置動態(tài)設置每個緩存的過期時間。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.time.Duration;
import java.util.Map;

/**
 * @author Tzx
 * @date 2022/12/13 19:33
 */
public class TaRedisCacheManager extends RedisCacheManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(TaRedisCacheManager.class);

    public TaRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }

    public TaRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
    }

    public TaRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {
        super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
    }

    public TaRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
    }

    public TaRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
    }

    @Override
    protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        MethodCacheExpireConfig cacheable = TaRedisCacheFactory.getCacheExpireConfig(name);
        if (null != cacheable && cacheable.getExpireTime() > 0) {
            cacheConfig = entryTtl(name, cacheable.getExpireTime(), cacheConfig);
        }
        return super.createRedisCache(name, cacheConfig);
    }

    private RedisCacheConfiguration entryTtl(String cacheName, long ttl, @Nullable RedisCacheConfiguration cacheConfig) {
        Assert.notNull(cacheConfig, "RedisCacheConfiguration is required; it must not be null");
        cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("redisCache {} 過期時間為{}秒", cacheName, ttl);
        }
        return cacheConfig;
    }

}

5、緩存自動刷新

RedisCacheget方法中,如果緩存未過期,檢查是否需要進行自動刷新。

    @Override
    public ValueWrapper get(@Nullable Object o) {
        if (null == o) {
            return null;
        }
        ValueWrapper wrapper = this.cache.get(o);
        // 刷新緩存
        if (null != wrapper) {
            SpringContextUtil.getApplicationContext().getBean(TaRedisCacheFactory.class).refreshCache(getName(),o.toString(), this::put);
        }
        return wrapper;
    }

6、TaRedisCacheFactory刷新策略

TaRedisCacheFactory負責緩存的刷新邏輯,確保緩存數(shù)據(jù)的實時性。

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.util.MethodInvoker;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author tangzx
 * @date 2022/12/17 11:09
 */
public class TaRedisCacheFactory {

    /**
     * 緩存過期配置
     */
    private static final ConcurrentHashMap<String, MethodCacheExpireConfig> CACHE_EXPIRE_CONFIG = new ConcurrentHashMap<>();

    private static final Logger LOGGER = LoggerFactory.getLogger(TaRedisCacheFactory.class);

    public TaRedisCacheFactory() {
        // document why this method is empty
    }

    public static void addCacheExpireConfig(String cacheName, MethodCacheExpireConfig methodCacheExpireConfig) {
        CACHE_EXPIRE_CONFIG.put(cacheName, methodCacheExpireConfig);
    }

    public static MethodCacheExpireConfig getCacheExpireConfig(String cacheName) {
        return CACHE_EXPIRE_CONFIG.get(cacheName);
    }

    /**
     * 刷新緩存
     *
     * @param cacheName 緩存名稱
     * @param cacheKey  緩存key
     */
    public void refreshCache(String cacheName, String cacheKey, RefreshCacheFunction f) {
        MethodCacheExpireConfig cacheable = getCacheExpireConfig(cacheName);
        if (null == cacheable) {
            return;
        }
        Class<?> targetClass = cacheable.getTarget();
        Method method = cacheable.getMethod();
        long expireRefreshTime = cacheable.getExpireRefreshTime();
        String redisKey = cacheName + cacheKey;
        long expire = RedisUtil.KeyOps.getExpire(redisKey);
        if (expire > expireRefreshTime) {
            return;
        }
        String argsStr = cacheKey.split("\\^")[1];
        Object[] args = JSON.parseObject(argsStr, Object[].class);
        if (null == args) {
            return;
        }
        try {
            // 創(chuàng)建方法執(zhí)行器
            MethodInvoker methodInvoker = new MethodInvoker();
            methodInvoker.setArguments(args);
            methodInvoker.setTargetClass(targetClass);
            methodInvoker.setTargetMethod(method.getName());
            methodInvoker.setTargetObject(AopProxyUtils.getSingletonTarget(SpringContextUtil.getApplicationContext().getBean(targetClass)));
            methodInvoker.prepare();
            Object invoke = methodInvoker.invoke();
            //然后設置進緩存和重新設置過期時間
            f.put(cacheKey, invoke);
            RedisUtil.KeyOps.expire(cacheKey, cacheable.getExpireTime(), TimeUnit.SECONDS);
        } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) {
            LOGGER.error("刷新緩存失敗:" + e.getMessage(), e);
        }

    }

}

7、MethodCacheExpireConfig

import lombok.Builder;
import lombok.Data;

import java.lang.reflect.Method;

/**
 * @author Tzx
 * @date 2022/12/17 11:10
 */
@Data
@Builder
public class MethodCacheExpireConfig {

    /**
     * 緩存過期時間
     */
    private long expireTime;
    /**
     * 緩存過期自動刷新閾值
     */
    private long expireRefreshTime;
    /**
     * 是否自動刷新
     */
    private boolean autoRefresh;
    /**
     * 類對象
     */
    private Class<?> target;
    /**
     * 緩存方法
     */
    private Method method;

}

8、RefreshCacheFunction

/**
 * @author tangzx
 */
@FunctionalInterface
public interface RefreshCacheFunction {

    /**
     * 緩存put
     *
     * @param key   key
     * @param value value
     */
    void put(String key, Object value);

}

9、DurationUtils

import java.time.Duration;

/**
 * @author Tzx
 * @date 2022/12/17 12:04
 */
public class DurationUtils {

    private DurationUtils(){
        // 2022/12/18
    }
    
    public static Duration parseDuration(String ttlStr) {
        String timeUnit = ttlStr.substring(ttlStr.length() - 1);
        switch (timeUnit) {
            case "d":
                return Duration.ofDays(parseLong(ttlStr));
            case "h":
                return Duration.ofHours(parseLong(ttlStr));
            case "m":
                return Duration.ofMinutes(parseLong(ttlStr));
            case "s":
                return Duration.ofSeconds(parseLong(ttlStr));
            default:
                return Duration.ofSeconds(Long.parseLong(ttlStr));
        }
    }

    private static long parseLong(String ttlStr) {
        return Long.parseLong(ttlStr.substring(0, ttlStr.length() - 1));
    }

}

到此這篇關于SpirngCache、Redis指定過期時間、到期自動刷新的文章就介紹到這了,更多相關SpirngCache、Redis指定過期時間 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • struts2實現(xiàn)文件下載功能

    struts2實現(xiàn)文件下載功能

    這篇文章主要為大家詳細介紹了struts2實現(xiàn)文件下載功能,一個非常常見的功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • SpringBoot整合Web開發(fā)之文件上傳與@ControllerAdvice

    SpringBoot整合Web開發(fā)之文件上傳與@ControllerAdvice

    @ControllerAdvice注解是Spring3.2中新增的注解,學名是Controller增強器,作用是給Controller控制器添加統(tǒng)一的操作或處理。對于@ControllerAdvice,我們比較熟知的用法是結合@ExceptionHandler用于全局異常的處理,但其作用不止于此
    2022-08-08
  • MyBatis-Plus執(zhí)行SQL分析打印過程

    MyBatis-Plus執(zhí)行SQL分析打印過程

    這篇文章主要介紹了MyBatis-Plus執(zhí)行SQL分析打印過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • mybatis模糊查詢之bind標簽和concat函數(shù)用法詳解

    mybatis模糊查詢之bind標簽和concat函數(shù)用法詳解

    大家都知道bind 標簽可以使用 OGNL 表達式創(chuàng)建一個變量井將其綁定到上下文中,接下來通過本文給大家介紹了mybatis模糊查詢——bind標簽和concat函數(shù)用法,需要的朋友可以參考下
    2022-08-08
  • spring boot啟動后直接關閉了的問題解決

    spring boot啟動后直接關閉了的問題解決

    本文主要介紹了spring boot啟動后直接關閉了的問題解決,SpringBoot項目啟動后自動關閉的原因是未引入web依賴,導致以普通Java項目運行,下面就來介紹一下解決方法,感興趣的可以了解一下
    2025-02-02
  • 深入理解Java設計模式之職責鏈模式

    深入理解Java設計模式之職責鏈模式

    這篇文章主要介紹了JAVA設計模式之職責鏈模式的的相關資料,文中示例代碼非常詳細,供大家參考和學習,感興趣的朋友可以了解
    2021-11-11
  • SpringBoot 中 AutoConfiguration的使用方法

    SpringBoot 中 AutoConfiguration的使用方法

    這篇文章主要介紹了SpringBoot 中 AutoConfiguration的使用方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-04-04
  • Java使用定時器編寫一個簡單的搶紅包小游戲

    Java使用定時器編寫一個簡單的搶紅包小游戲

    這篇文章主要為大家介紹了Java如何使用定時器編寫一個簡單的搶紅包小游戲,文中的示例代碼講解詳細,感興趣的小伙伴可以嘗試一下
    2022-07-07
  • 淺談Hibernate對象狀態(tài)之間的神奇轉換

    淺談Hibernate對象狀態(tài)之間的神奇轉換

    這篇文章主要介紹了淺談Hibernate對象狀態(tài)之間的神奇轉換,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • C++實現(xiàn)掃雷游戲示例講解

    C++實現(xiàn)掃雷游戲示例講解

    這篇文章主要介紹了C++實現(xiàn)掃雷游戲示例講解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08

最新評論