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

SpringCache結(jié)合Redis實(shí)現(xiàn)指定過期時(shí)間和到期自動(dòng)刷新

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

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

1、自定義注解CacheExpireConfig

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

import java.lang.annotation.*;

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

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

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

}

 2、使用注解

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

    @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、啟動(dòng)時(shí)加載緩存過期配置

在Spring Boot應(yīng)用啟動(dòng)時(shí),通過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) {
                // 如果方法上未同時(shí)注解@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àng)目中未使用工具類,可自行實(shí)現(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,設(shè)置過期時(shí)間

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

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 {} 過期時(shí)間為{}秒", cacheName, ttl);
        }
        return cacheConfig;
    }

}

5、緩存自動(dòng)刷新

RedisCacheget方法中,如果緩存未過期,檢查是否需要進(jìn)行自動(dòng)刷新。

    @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負(fù)責(zé)緩存的刷新邏輯,確保緩存數(shù)據(jù)的實(shí)時(shí)性。

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();
            //然后設(shè)置進(jìn)緩存和重新設(shè)置過期時(shí)間
            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 {

    /**
     * 緩存過期時(shí)間
     */
    private long expireTime;
    /**
     * 緩存過期自動(dòng)刷新閾值
     */
    private long expireRefreshTime;
    /**
     * 是否自動(dòng)刷新
     */
    private boolean autoRefresh;
    /**
     * 類對(duì)象
     */
    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));
    }

}

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

    spring boot啟動(dòng)后直接關(guān)閉了的問題解決

    本文主要介紹了spring boot啟動(dòng)后直接關(guān)閉了的問題解決,SpringBoot項(xiàng)目啟動(dòng)后自動(dòng)關(guān)閉的原因是未引入web依賴,導(dǎo)致以普通Java項(xiàng)目運(yùn)行,下面就來介紹一下解決方法,感興趣的可以了解一下
    2025-02-02
  • 深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

    深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

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

    SpringBoot 中 AutoConfiguration的使用方法

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

    Java使用定時(shí)器編寫一個(gè)簡(jiǎn)單的搶紅包小游戲

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

    淺談Hibernate對(duì)象狀態(tài)之間的神奇轉(zhuǎn)換

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

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

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

最新評(píng)論