亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

基于spring-cache實現多級緩存

2024-06-04 09:06:51
28
0

一、基于spring-cache實現多級緩存

  • 實現效果,完全兼用原有SpringCache緩存使用方式和相關注解
  • 通過自定義注解,驅逐的相關緩存層的數據
  • 無須后續再處理多級緩存邏輯

二、具體實現代碼

1、引入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.8.8</version>
</dependency>

2、自定義緩存控制器

package top.xy23.cache;
?
import lombok.Getter;
import lombok.NonNull;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
?
/**
 * 兩級緩存控制器
 * 一級緩存:本地緩存
 * 二級緩存:Redis緩存
 * author: XiaoYang
 * date: 2024/5/31 下午5:16
 */
@Getter
public class TwoLevelCacheManager implements CacheManager {
    private static final Logger log = LoggerFactory.getLogger(TwoLevelCacheManager.class);
    private final CacheManager primaryCacheManager;
    private final CacheManager secondaryCacheManager;
?
    public TwoLevelCacheManager(CacheManager primary, CacheManager secondary) {
        this.primaryCacheManager = primary;
        this.secondaryCacheManager = secondary;
    }
?
    @Override
    public Cache getCache(@NonNull String name) {
        Cache primary = primaryCacheManager.getCache(name);
        Cache secondary = secondaryCacheManager.getCache(name);
        if (primary == null || secondary == null) {
            log.warn("未找到名為 {} 的緩存", name);
            return null;
        }
        return new TwoLevelCache(primary, secondary);
    }
?
    @Override
    public @NonNull Collection<String> getCacheNames() {
        List<String> cacheNames = new ArrayList<>();
        cacheNames.addAll(primaryCacheManager.getCacheNames());
        cacheNames.addAll(secondaryCacheManager.getCacheNames());
        return cacheNames;
    }
}

3、自定義緩存實現

package top.xy23.cache;
?
import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
?
import java.util.concurrent.Callable;
?
/**
 * 緩存層
 * author: XiaoYang
 * date: 2024/6/4 下午2:01
 */
public class TwoLevelCache implements Cache {
    private static final Logger log = LoggerFactory.getLogger(TwoLevelCache.class);
?
    private final Cache primary;
    private final Cache secondary;
?
    public TwoLevelCache(Cache primary, Cache secondary) {
        this.primary = primary;
        this.secondary = secondary;
    }
?
    @Override
    public @NonNull String getName() {
        return primary.getName();
    }
?
    @Override
    public @NonNull Object getNativeCache() {
        return primary.getNativeCache();
    }
?
    @Override
    public ValueWrapper get(@NonNull Object key) {
        ValueWrapper value = primary.get(key);
        if (value == null) {
            value = secondary.get(key);
            if (value != null) {
                primary.put(key, value.get());
            }
        }
        return value;
    }
?
    @Override
    public <T> T get(@NonNull Object key, Class<T> type) {
        T value = primary.get(key, type);
        if (value == null) {
            value = secondary.get(key, type);
            if (value != null) {
                primary.put(key, value);
            }
        }
        return value;
    }
?
    @Override
    public @NonNull <T> T get(@NonNull Object key,@NonNull Callable<T> valueLoader) {
        T value = primary.get(key, valueLoader);
        if (value == null) {
            try {
                value = valueLoader.call();
                secondary.put(key, value);
                primary.put(key, value);
            } catch (Exception e) {
                log.error("加載緩存值失敗,鍵為 {}", key, e);
                throw new ValueRetrievalException(key, valueLoader, e);
            }
        }
        return value;
    }
?
    @Override
    public void put(@NonNull Object key, Object value) {
        primary.put(key, value);
        secondary.put(key, value);
    }
?
    @Override
    public void evict(@NonNull Object key) {
        primary.evict(key);
        secondary.evict(key);
    }
?
    @Override
    public void clear() {
        primary.clear();
        secondary.clear();
    }
}

4、將自定義的緩存管理器注入Spring容器

package top.xy23.config;
?
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.EnableCaching;
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 org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import top.xy23.cache.TwoLevelCacheManager;
?
import java.time.Duration;
?
@Configuration
@EnableCaching
public class CacheConfig {
?
    @Bean
    public Caffeine<Object, Object> caffeineConfig() {
        return Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofSeconds(30))
                .maximumSize(100);
    }
?
    @Bean
    @Primary
    public TwoLevelCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, Caffeine<Object, Object> caffeine) {
        // 本地緩存使用 Caffeine
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(caffeine);
?
        // redis緩存
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1));  // 設置默認的expire時間為1小時
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
?
        // 自定義緩存管理器
        return new TwoLevelCacheManager(caffeineCacheManager, redisCacheManager);
    }
}

5、可選-自定義驅逐緩存注解-用于精細控制緩存層

5.1、自定義注解-TwoLevelCacheEvict

package top.xy23.cache.aop;
?
import org.springframework.core.annotation.AliasFor;
import top.xy23.cache.CacheType;
?
import java.lang.annotation.*;
?
/**
 * 自定義緩存驅逐器用于設置要驅逐的緩存
 * author: XiaoYang
 * date: 2024/6/4 上午8:42
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TwoLevelCacheEvict {
    /**
     * 要驅逐的緩存類型,默認為全部
     */
    CacheType cacheType() default CacheType.ALL;
?
    /**
     * Alias for {@link #cacheNames}.
     */
    @AliasFor("cacheNames")
    String[] value() default {};
?
    /**
     * 用于緩存逐出操作的緩存的名稱。
     * 名稱可用于確定目標高速緩存(或高速緩存),與特定 Bean 定義的限定符值或 Bean 名稱匹配
     */
    @AliasFor("value")
    String[] cacheNames() default {};
?
    /**
     Spring 表達式語言 (SpEL) 表達式,用于動態計算密鑰。
     默認值為 "",表示除非設置了自定義 keyGenerator 量,否則所有方法參數都被視為鍵。
     SpEL 表達式根據提供以下元數據的專用上下文進行計算:
     #result作為對方法調用結果的引用,只有在 is false時才能使用beforeInvocation()。對于支持的包裝器,例如 Optional,#result指的是實際對象,而不是包裝器
     #root. method、 #root. target和 #root. caches 分別用于對 method、目標對象和受影響緩存的引用。
     方法名稱 (#root. methodName) 和目標類 (#root. targetClass) 的快捷方式也可用。
     方法參數可以通過索引訪問。例如,可以通過 或 #p1 #a1訪問#root. args[1]第二個參數。如果該信息可用,也可以按名稱訪問參數。
     */
    String key() default "";
?
    /**
     * 是否刪除緩存中的所有條目。
     * <p>默認情況下,僅刪除關聯鍵下的值。
     * <p>請注意,將此參數設置為 {@code true} 并指定
     * {@link #key} 是不允許的。
     */
    boolean allEntries() default false;
}

5.2、自定義切面-處理TwoLevelCacheEvict注解

package top.xy23.cache.aop;
?
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import top.xy23.cache.CacheType;
import top.xy23.cache.TwoLevelCacheManager;
?
/**
 * 自定義緩存清除切面
 * author: XiaoYang
 * date: 2024/6/4 上午8:58
 */
@Aspect
@Component
@Slf4j
public class TwoLeveCacheEvictAspect {
    @Autowired
    private TwoLevelCacheManager twoLevelCacheManager;
?
    @Around("@annotation(twoLevelCacheEvict)")
    public Object evictCache(ProceedingJoinPoint pjp, TwoLevelCacheEvict twoLevelCacheEvict) throws Throwable {
        evictCaches(twoLevelCacheEvict);
        return pjp.proceed();
    }
?
    private void evictCaches(TwoLevelCacheEvict twoLevelCacheEvict) {
        CacheManager cacheManager;
        CacheType cacheType = twoLevelCacheEvict.cacheType();
        switch (cacheType) {
            case PRIMARY:
                log.info("Evicting primary caches");
                cacheManager = twoLevelCacheManager.getPrimaryCacheManager();
                break;
            case SECONDARY:
                log.info("Evicting secondary caches");
                cacheManager = twoLevelCacheManager.getSecondaryCacheManager();
                break;
            case ALL:
                log.info("Evicting all caches");
                cacheManager = twoLevelCacheManager;
                break;
            default:
                throw new IllegalArgumentException();
        }
?
        for (String cacheName : twoLevelCacheEvict.cacheNames()) {
            Cache cache = cacheManager.getCache(cacheName);
            if (cache == null) {
                continue;
            }
            try {
                if (twoLevelCacheEvict.allEntries()) {
                    cache.clear();
                } else {
                    cache.evict(twoLevelCacheEvict.key());
                }
            } catch (Exception ignored) {
            }
        }
    }
}

5.3、緩存層級枚舉類

package top.xy23.cache;
?
/**
 * author: XiaoYang
 * date: 2024/6/4 上午8:54
 */
public enum CacheType {
    /**
     * 全部緩存
     */
    ALL,
    /**
     * 主緩存/一級緩存
     */
    PRIMARY,
    /**
     * 二級緩存
     */
    SECONDARY;
}

6、未完-繼續完善自定義put相關注解-用于精細控制緩存層

0條評論
0 / 1000
曉陽
1文章數
0粉絲數
曉陽
1 文章 | 0 粉絲
曉陽
1文章數
0粉絲數
曉陽
1 文章 | 0 粉絲
原創

基于spring-cache實現多級緩存

2024-06-04 09:06:51
28
0

一、基于spring-cache實現多級緩存

  • 實現效果,完全兼用原有SpringCache緩存使用方式和相關注解
  • 通過自定義注解,驅逐的相關緩存層的數據
  • 無須后續再處理多級緩存邏輯

二、具體實現代碼

1、引入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.8.8</version>
</dependency>

2、自定義緩存控制器

package top.xy23.cache;
?
import lombok.Getter;
import lombok.NonNull;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
?
/**
 * 兩級緩存控制器
 * 一級緩存:本地緩存
 * 二級緩存:Redis緩存
 * author: XiaoYang
 * date: 2024/5/31 下午5:16
 */
@Getter
public class TwoLevelCacheManager implements CacheManager {
    private static final Logger log = LoggerFactory.getLogger(TwoLevelCacheManager.class);
    private final CacheManager primaryCacheManager;
    private final CacheManager secondaryCacheManager;
?
    public TwoLevelCacheManager(CacheManager primary, CacheManager secondary) {
        this.primaryCacheManager = primary;
        this.secondaryCacheManager = secondary;
    }
?
    @Override
    public Cache getCache(@NonNull String name) {
        Cache primary = primaryCacheManager.getCache(name);
        Cache secondary = secondaryCacheManager.getCache(name);
        if (primary == null || secondary == null) {
            log.warn("未找到名為 {} 的緩存", name);
            return null;
        }
        return new TwoLevelCache(primary, secondary);
    }
?
    @Override
    public @NonNull Collection<String> getCacheNames() {
        List<String> cacheNames = new ArrayList<>();
        cacheNames.addAll(primaryCacheManager.getCacheNames());
        cacheNames.addAll(secondaryCacheManager.getCacheNames());
        return cacheNames;
    }
}

3、自定義緩存實現

package top.xy23.cache;
?
import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
?
import java.util.concurrent.Callable;
?
/**
 * 緩存層
 * author: XiaoYang
 * date: 2024/6/4 下午2:01
 */
public class TwoLevelCache implements Cache {
    private static final Logger log = LoggerFactory.getLogger(TwoLevelCache.class);
?
    private final Cache primary;
    private final Cache secondary;
?
    public TwoLevelCache(Cache primary, Cache secondary) {
        this.primary = primary;
        this.secondary = secondary;
    }
?
    @Override
    public @NonNull String getName() {
        return primary.getName();
    }
?
    @Override
    public @NonNull Object getNativeCache() {
        return primary.getNativeCache();
    }
?
    @Override
    public ValueWrapper get(@NonNull Object key) {
        ValueWrapper value = primary.get(key);
        if (value == null) {
            value = secondary.get(key);
            if (value != null) {
                primary.put(key, value.get());
            }
        }
        return value;
    }
?
    @Override
    public <T> T get(@NonNull Object key, Class<T> type) {
        T value = primary.get(key, type);
        if (value == null) {
            value = secondary.get(key, type);
            if (value != null) {
                primary.put(key, value);
            }
        }
        return value;
    }
?
    @Override
    public @NonNull <T> T get(@NonNull Object key,@NonNull Callable<T> valueLoader) {
        T value = primary.get(key, valueLoader);
        if (value == null) {
            try {
                value = valueLoader.call();
                secondary.put(key, value);
                primary.put(key, value);
            } catch (Exception e) {
                log.error("加載緩存值失敗,鍵為 {}", key, e);
                throw new ValueRetrievalException(key, valueLoader, e);
            }
        }
        return value;
    }
?
    @Override
    public void put(@NonNull Object key, Object value) {
        primary.put(key, value);
        secondary.put(key, value);
    }
?
    @Override
    public void evict(@NonNull Object key) {
        primary.evict(key);
        secondary.evict(key);
    }
?
    @Override
    public void clear() {
        primary.clear();
        secondary.clear();
    }
}

4、將自定義的緩存管理器注入Spring容器

package top.xy23.config;
?
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.EnableCaching;
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 org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import top.xy23.cache.TwoLevelCacheManager;
?
import java.time.Duration;
?
@Configuration
@EnableCaching
public class CacheConfig {
?
    @Bean
    public Caffeine<Object, Object> caffeineConfig() {
        return Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofSeconds(30))
                .maximumSize(100);
    }
?
    @Bean
    @Primary
    public TwoLevelCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, Caffeine<Object, Object> caffeine) {
        // 本地緩存使用 Caffeine
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(caffeine);
?
        // redis緩存
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1));  // 設置默認的expire時間為1小時
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
?
        // 自定義緩存管理器
        return new TwoLevelCacheManager(caffeineCacheManager, redisCacheManager);
    }
}

5、可選-自定義驅逐緩存注解-用于精細控制緩存層

5.1、自定義注解-TwoLevelCacheEvict

package top.xy23.cache.aop;
?
import org.springframework.core.annotation.AliasFor;
import top.xy23.cache.CacheType;
?
import java.lang.annotation.*;
?
/**
 * 自定義緩存驅逐器用于設置要驅逐的緩存
 * author: XiaoYang
 * date: 2024/6/4 上午8:42
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TwoLevelCacheEvict {
    /**
     * 要驅逐的緩存類型,默認為全部
     */
    CacheType cacheType() default CacheType.ALL;
?
    /**
     * Alias for {@link #cacheNames}.
     */
    @AliasFor("cacheNames")
    String[] value() default {};
?
    /**
     * 用于緩存逐出操作的緩存的名稱。
     * 名稱可用于確定目標高速緩存(或高速緩存),與特定 Bean 定義的限定符值或 Bean 名稱匹配
     */
    @AliasFor("value")
    String[] cacheNames() default {};
?
    /**
     Spring 表達式語言 (SpEL) 表達式,用于動態計算密鑰。
     默認值為 "",表示除非設置了自定義 keyGenerator 量,否則所有方法參數都被視為鍵。
     SpEL 表達式根據提供以下元數據的專用上下文進行計算:
     #result作為對方法調用結果的引用,只有在 is false時才能使用beforeInvocation()。對于支持的包裝器,例如 Optional,#result指的是實際對象,而不是包裝器
     #root. method、 #root. target和 #root. caches 分別用于對 method、目標對象和受影響緩存的引用。
     方法名稱 (#root. methodName) 和目標類 (#root. targetClass) 的快捷方式也可用。
     方法參數可以通過索引訪問。例如,可以通過 或 #p1 #a1訪問#root. args[1]第二個參數。如果該信息可用,也可以按名稱訪問參數。
     */
    String key() default "";
?
    /**
     * 是否刪除緩存中的所有條目。
     * <p>默認情況下,僅刪除關聯鍵下的值。
     * <p>請注意,將此參數設置為 {@code true} 并指定
     * {@link #key} 是不允許的。
     */
    boolean allEntries() default false;
}

5.2、自定義切面-處理TwoLevelCacheEvict注解

package top.xy23.cache.aop;
?
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import top.xy23.cache.CacheType;
import top.xy23.cache.TwoLevelCacheManager;
?
/**
 * 自定義緩存清除切面
 * author: XiaoYang
 * date: 2024/6/4 上午8:58
 */
@Aspect
@Component
@Slf4j
public class TwoLeveCacheEvictAspect {
    @Autowired
    private TwoLevelCacheManager twoLevelCacheManager;
?
    @Around("@annotation(twoLevelCacheEvict)")
    public Object evictCache(ProceedingJoinPoint pjp, TwoLevelCacheEvict twoLevelCacheEvict) throws Throwable {
        evictCaches(twoLevelCacheEvict);
        return pjp.proceed();
    }
?
    private void evictCaches(TwoLevelCacheEvict twoLevelCacheEvict) {
        CacheManager cacheManager;
        CacheType cacheType = twoLevelCacheEvict.cacheType();
        switch (cacheType) {
            case PRIMARY:
                log.info("Evicting primary caches");
                cacheManager = twoLevelCacheManager.getPrimaryCacheManager();
                break;
            case SECONDARY:
                log.info("Evicting secondary caches");
                cacheManager = twoLevelCacheManager.getSecondaryCacheManager();
                break;
            case ALL:
                log.info("Evicting all caches");
                cacheManager = twoLevelCacheManager;
                break;
            default:
                throw new IllegalArgumentException();
        }
?
        for (String cacheName : twoLevelCacheEvict.cacheNames()) {
            Cache cache = cacheManager.getCache(cacheName);
            if (cache == null) {
                continue;
            }
            try {
                if (twoLevelCacheEvict.allEntries()) {
                    cache.clear();
                } else {
                    cache.evict(twoLevelCacheEvict.key());
                }
            } catch (Exception ignored) {
            }
        }
    }
}

5.3、緩存層級枚舉類

package top.xy23.cache;
?
/**
 * author: XiaoYang
 * date: 2024/6/4 上午8:54
 */
public enum CacheType {
    /**
     * 全部緩存
     */
    ALL,
    /**
     * 主緩存/一級緩存
     */
    PRIMARY,
    /**
     * 二級緩存
     */
    SECONDARY;
}

6、未完-繼續完善自定義put相關注解-用于精細控制緩存層

文章來自個人專欄
文章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
0
0