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

Redis+自定義注解+AOP實(shí)現(xiàn)聲明式注解緩存查詢的示例

 更新時(shí)間:2025年04月02日 10:18:20   作者:????????鏟子Zzz  
實(shí)際項(xiàng)目中,會(huì)遇到很多查詢數(shù)據(jù)的場(chǎng)景,這些數(shù)據(jù)更新頻率也不是很高,一般我們?cè)跇I(yè)務(wù)處理時(shí),會(huì)對(duì)這些數(shù)據(jù)進(jìn)行緩存,本文主要介紹了Redis+自定義注解+AOP實(shí)現(xiàn)聲明式注解緩存查詢的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

引言:為什么需要聲明式緩存?

  • 背景痛點(diǎn):傳統(tǒng)代碼中緩存邏輯與業(yè)務(wù)邏輯高度耦合,存在重復(fù)代碼、維護(hù)困難等問(wèn)題(如手動(dòng)判斷緩存存在性、序列化/反序列化操作) 
  • 解決方案:通過(guò)注解+AOP實(shí)現(xiàn)緩存邏輯與業(yè)務(wù)解耦,開發(fā)者只需關(guān)注業(yè)務(wù),通過(guò)注解配置緩存策略(如過(guò)期時(shí)間、防擊穿機(jī)制等) 
  • 技術(shù)價(jià)值:提升代碼可讀性、降低維護(hù)成本、支持動(dòng)態(tài)緩存策略擴(kuò)展。

核心流程設(shè)計(jì)

方法調(diào)用 → 切面攔截 → 生成緩存Key → 查詢Redis → 
└ 命中 → 直接返回緩存數(shù)據(jù)
└ 未命中 → 加鎖查DB → 結(jié)果寫入Redis → 返回?cái)?shù)據(jù)

二、核心實(shí)現(xiàn)步驟

1. 定義自定義緩存注解(如@RedisCache)

package com.mixchains.ytboot.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * @author 衛(wèi)相yang
 * OverSion03
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
    /**
     * Redis鍵前綴(支持SpEL表達(dá)式)
     */
    String key();

    /**
     * 過(guò)期時(shí)間(默認(rèn)1天)
     */
    long expire() default 1;

    /**
     * 時(shí)間單位(默認(rèn)天)
     */
    TimeUnit timeUnit() default TimeUnit.DAYS;

    /**
     * 是否緩存空值(防穿透)
     */
    boolean cacheNull() default true;
}

2. 編寫AOP切面(核心邏輯)

切面職責(zé)

  • 緩存Key生成:拼接類名、方法名、參數(shù)哈希(MD5或SpEL動(dòng)態(tài)參數(shù))本次使用的是SpEL
  • 緩存查詢:優(yōu)先從Redis讀取,使用FastJson等工具反序列化  

空值緩存:緩存NULL值并設(shè)置短過(guò)期時(shí)間,防止惡意攻擊

package com.mixchains.ytboot.common.aspect;

import com.alibaba.fastjson.JSON;
import com.mixchains.ytboot.common.annotation.RedisCache;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * @author 衛(wèi)相yang
 * OverSion03
 */
@Aspect
@Component
@Slf4j
public class RedisCacheAspect {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    @Around("@annotation(redisCache)")
    public Object around(ProceedingJoinPoint joinPoint, RedisCache redisCache) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 解析SpEL表達(dá)式生成完整key
        String key = parseKey(redisCache.key(), method, joinPoint.getArgs());
        // 嘗試從緩存獲取
        String cachedValue = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(cachedValue)) {
            Type returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();
            return JSON.parseObject(cachedValue, returnType);
        }
        // 執(zhí)行原方法
        Object result = joinPoint.proceed();
        // 處理緩存存儲(chǔ)
        if (result != null || redisCache.cacheNull()) {
            String valueToCache = result != null ?
                    JSON.toJSONString(result) :
                    (redisCache.cacheNull() ? "[]" : null);

            if (valueToCache != null) {
                redisTemplate.opsForValue().set(
                        key,
                        valueToCache,
                        redisCache.expire(),
                        redisCache.timeUnit()
                );
            }
        }
        return result;
    }

    private String parseKey(String keyTemplate, Method method, Object[] args) {
        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }
        return parser.parseExpression(keyTemplate).getValue(context, String.class);
    }
}

代碼片段示例

 @RedisCache(
            key = "'category:homeSecond:' + #categoryType",  //緩存的Key + 動(dòng)態(tài)參數(shù)
            expire = 1, //過(guò)期時(shí)間
            timeUnit = TimeUnit.DAYS // 時(shí)間單位
    )
    @Override
    public ReturnVO<List<GoodsCategory>> listHomeSecondGoodsCategory(Integer level, Integer categoryType) {
        // 數(shù)據(jù)庫(kù)查詢
        List<GoodsCategory> dbList = goodsCategoryMapper.selectList(
                new LambdaQueryWrapper<GoodsCategory>()
                        .eq(GoodsCategory::getCategoryLevel, level)
                        .eq(GoodsCategory::getCategoryType, categoryType)
                        .eq(GoodsCategory::getIsHomePage, 1)
                        .orderByDesc(GoodsCategory::getHomeSort)
        );
        // 設(shè)置父級(jí)UUID(可優(yōu)化為批量查詢)
        List<Long> parentIds = dbList.stream().map(GoodsCategory::getParentId).distinct().collect(Collectors.toList());
        Map<Long, String> parentMap = goodsCategoryMapper.selectBatchIds(parentIds)
                .stream()
                .collect(Collectors.toMap(GoodsCategory::getId, GoodsCategory::getUuid));

        dbList.forEach(item -> item.setParentUuid(parentMap.get(item.getParentId())));
        return ReturnVO.ok("列出首頁(yè)二級(jí)分類", dbList);
    }

最終效果:

到此這篇關(guān)于Redis+自定義注解+AOP實(shí)現(xiàn)聲明式注解緩存查詢的示例的文章就介紹到這了,更多相關(guān)Redis 聲明式注解緩存查詢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis的配置、啟動(dòng)、操作和關(guān)閉方法

    Redis的配置、啟動(dòng)、操作和關(guān)閉方法

    今天小編就為大家分享一篇Redis的配置、啟動(dòng)、操作和關(guān)閉方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • 一文帶你了解Redis的三種集群模式

    一文帶你了解Redis的三種集群模式

    Redis?的常用的集群方式主要有以下三種,分別是主從復(fù)制模式、哨兵模式、Redis-Cluster集群模式,那么下面我們就分別了解一下這三種集群模式的優(yōu)點(diǎn)與缺點(diǎn)
    2023-06-06
  • 詳解redis中的鎖以及使用場(chǎng)景

    詳解redis中的鎖以及使用場(chǎng)景

    這篇文章主要介紹了詳解redis中的鎖以及使用場(chǎng)景,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • redis哨兵模式說(shuō)明與搭建詳解

    redis哨兵模式說(shuō)明與搭建詳解

    這篇文章主要介紹了redis哨兵模式說(shuō)明與搭建詳解,需要的朋友可以參考下
    2023-01-01
  • redis 實(shí)現(xiàn)登陸次數(shù)限制的思路詳解

    redis 實(shí)現(xiàn)登陸次數(shù)限制的思路詳解

    這篇文章主要介紹了redis 實(shí)現(xiàn)登陸次數(shù)限制的思路詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 關(guān)于Redis?bigkeys命令會(huì)阻塞問(wèn)題的解決

    關(guān)于Redis?bigkeys命令會(huì)阻塞問(wèn)題的解決

    這篇文章主要介紹了關(guān)于Redis?bigkeys命令會(huì)阻塞問(wèn)題的解決,今天分享一次Redis引發(fā)的線上事故,避免再次踩雷,實(shí)現(xiàn)快速入門,需要的朋友可以參考下
    2023-03-03
  • redis發(fā)布和訂閱_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    redis發(fā)布和訂閱_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了redis發(fā)布和訂閱的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • 一文教你學(xué)會(huì)Redis的事務(wù)

    一文教你學(xué)會(huì)Redis的事務(wù)

    Redis?作為內(nèi)存的存儲(chǔ)中間件,已經(jīng)是面試的面試題必問(wèn)之一了。今天小編就來(lái)和大家一起來(lái)聊聊Redis的事務(wù)吧,希望對(duì)大家有所幫助
    2022-08-08
  • Redis消息隊(duì)列實(shí)現(xiàn)異步秒殺功能

    Redis消息隊(duì)列實(shí)現(xiàn)異步秒殺功能

    在高并發(fā)場(chǎng)景下,為了提高秒殺業(yè)務(wù)的性能,可將部分工作交給 Redis 處理,并通過(guò)異步方式執(zhí)行,Redis 提供了多種數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)消息隊(duì)列,總結(jié)三種,本文詳細(xì)介紹Redis消息隊(duì)列實(shí)現(xiàn)異步秒殺功能,感興趣的朋友一起看看吧
    2025-04-04
  • Redis緩存過(guò)期的實(shí)現(xiàn)示例

    Redis緩存過(guò)期的實(shí)現(xiàn)示例

    Redis緩存的過(guò)期策略是保證緩存可靠性和性能的關(guān)鍵之一,本文主要介紹了Redis緩存過(guò)期的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12

最新評(píng)論