最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Cache抽象-使用SpEL表達式解析

 更新時間:2021年12月23日 12:02:09   作者:小小工匠  
這篇文章主要介紹了Spring?Cache抽象-使用SpEL表達式解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Spring Cache抽象-使用SpEL表達式

概述

在Spring Cache注解屬性中(比如key,condition和unless),Spring的緩存抽象使用了SpEl表達式,從而提供了屬性值的動態(tài)生成及足夠的靈活性。

下面的代碼根據(jù)用戶的userCode進行緩存,對于key屬性,使用了表達式自定義鍵的生成。

public class UserService {
    private Map<Integer, User> users = new HashMap<Integer, User>();
    {
        users.put(1, new User("1", "w1",37));
        users.put(2, new User("2", "w2", 34));
    }
    @Cacheable(value = "users", key = "#user.userCode" condition = "#user.age < 35")
    public User getUser(User user) {
        System.out.println("User with id " + user.getUserId() + " requested.");
        return users.get(Integer.valueOf(user.getUserId()));
    }

SpEl表達式

SpEL表達式可基于上下文并通過使用緩存抽象,提供與root獨享相關聯(lián)的緩存特定的內(nèi)置參數(shù)。

名稱 位置 描述 示例
methodName root對象 當前被調(diào)用的方法名 #root.methodname
method root對象 當前被調(diào)用的方法 #root.method.name
target root對象 當前被調(diào)用的目標對象實例 #root.target
targetClass root對象 當前被調(diào)用的目標對象的類 #root.targetClass
args root對象 當前被調(diào)用的方法的參數(shù)列表 #root.args[0]
caches root對象 當前方法調(diào)用使用的緩存列表 #root.caches[0].name
Argument Name 執(zhí)行上下文 當前被調(diào)用的方法的參數(shù),如findArtisan(Artisan artisan),可以通過#artsian.id獲得參數(shù) #artsian.id
result 執(zhí)行上下文 方法執(zhí)行后的返回值(僅當方法執(zhí)行后的判斷有效,如 unless cacheEvict的beforeInvocation=false) #result

如何讓自定義注解支持SpEL表達式

  • SpEL:即Spring Expression Language,是一種強大的表達式語言。在Spring產(chǎn)品組合中,它是表達式計算的基礎。它支持在運行時查詢和操作對象圖,它可以與基于XML和基于注解的Spring配置還有bean定義一起使用。由于它能夠在運行時動態(tài)分配值,因此可以為我們節(jié)省大量Java代碼。可以用于解析特殊字符串(比如Bean的屬性可以直接在字符串中的點出來)。
  • SpEL的應用:常見的應用,如注解上的使用,Spring緩存中使用的注解
    @cachable(key="#user.uId")
    public User createUser(User user) {
     return user;
    }

SpEL還可以用在xml等等上面的解析,大家可以去查閱相關資料。本文主要介紹如果將SpEl與自定義注解相結合,從而解析出自定義注解value的實際值。

  • Spring緩存操作起來非常方便,只需要加上注解便可實現(xiàn),Spring也提供的CacheManager,使用者可以配置Redis使用Redis緩存。Spring注解也支持自定義的Key命名,功能已經(jīng)挺齊全了。
  • 但是,如果想要更多的自定義緩存數(shù)據(jù)存儲格式,比如說緩存的數(shù)據(jù)之間是有層次關系的(比如視頻稿件包含視頻,視頻下面又包含了視頻彈幕),此時想要在更新最頂層的視頻彈幕時,不刪除整個緩存,而只是更新某個視頻稿件下的某個視頻的這一條視頻彈幕,Spring提供的緩存注解似乎有點不夠用。
  • 此時有的開發(fā)者可能會想到使用自定義注解+AOP+Jedis來更加細分緩存的存儲結構,但是又想用到強大的SpEL表達式來為自定義注解的值賦值(不使用SpEL的話,需要在AOP中獲取入?yún)⒒蛘叻祷刂?,但是每個方法的數(shù)據(jù)類型又不相同,想要拿到特定的值,便需要類型判斷-轉換),此時便可以使用SpEL提供的SpelExpressionParser工具來進行解析注解的值,使用十分方便,只需按照SpEL的規(guī)則(#)來書寫即可。

使用方法

generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint)方法封裝了SpelExpressionParser解析SpEL的方法,使用時只需要傳入spELString:注解的值以及AOP的 joinPoint即可,SpelExpressionParser便會自動的為我們解析出注解的實際值

     /**
     * 用于SpEL表達式解析.
     */
    private SpelExpressionParser parser = new SpelExpressionParser();
    /**
     * 用于獲取方法參數(shù)定義名字.
     */
    private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
    
    public String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
        // 通過joinPoint獲取被注解方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 使用spring的DefaultParameterNameDiscoverer獲取方法形參名數(shù)組
        String[] paramNames = nameDiscoverer.getParameterNames(method);
        // 解析過后的Spring表達式對象
        Expression expression = parser.parseExpression(spELString);
        // spring的表達式上下文對象
        EvaluationContext context = new StandardEvaluationContext();
        // 通過joinPoint獲取被注解方法的形參
        Object[] args = joinPoint.getArgs();
        // 給上下文賦值
        for(int i = 0 ; i < args.length ; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        // 表達式從上下文中計算出實際參數(shù)值
        /*如:
            @annotation(key="#student.name")
             method(Student student)
             那么就可以解析出方法形參的某屬性值,return “xiaoming”;
          */
        return expression.getValue(context).toString();
    }

使用案例

1.準備

①.SpringAop相關jar包,

②.Spring-expression

2.自定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SelectRedisCache {
    String key(); //Redis緩存的HK
    String fieldKey() ; //Redis緩存的K
    //默認十個小時清空
    int expireTime() default 36000;
}

3.定義AOP攔截注解對方法增強進行讀寫緩存

@Aspect
public class SelectRedisCacheAop extends SPELUtil {
 private Map<String,Map<String,Object>> redisMap = new HashMap();
    @Around("execution(@com.XliXli.annotation.consuRedisCache.SelectRedisCache * *.*(..)) && @annotation(cacheable)")
    public Object aroundCacheable(ProceedingJoinPoint joinPoint, SelectRedisCache cacheable) throws Throwable {
        //首先獲取注解的實際值,如果是SpEl表達式則進行解析
        String key = "";
        String fieldKey = "";
        Object redisObj = null;
        try {
            if (!cacheable.key().contains("#")) {
                //注解的值非SPEL表達式,直接解析就好
                key = cacheable.key();
            } else {
                //使用注解中的key, 支持SpEL表達式
                String spEL = cacheable.key();
                //調(diào)用SpelExpressionParser方法解析出注解的實際值
                key = generateKeyBySpEL(spEL, joinPoint);
                System.out.println("key=" +key);
            }
            //獲取fieldKey,同上面的key一樣
            if (cacheable.fieldKey().equals("")) {
                //等于空,則查詢整個大Key
                fieldKey = "SelectString";
            } else {
                //使用注解中的key, 支持SpEL表達式
                String spEL = cacheable.fieldKey();
                fieldKey = generateKeyBySpEL(spEL, joinPoint);
            }
   //如果注解的fieldKey值為"",則查詢大Key
            if (fieldKey.equals("SelectString")) {
                //直接查詢的是大Key
                Set keys = redisMap.get(key).keySet();
                //使用集合來接收查詢出來的對象
                List<Object>  redisList = new ArrayList<>();
                //遍歷緩存fieldKey,查出緩存中每一個對象,放入redisList中
                for (Object fieldKey2 : keys) {
                    Object innerObj = redisMap.get(key).get(fieldKey2);
                    redisList.add(innerObj);
                }
                redisObj = redisList;
                if (redisList == null || redisList.size() <1) {
                    redisObj = null;
                }
            } else {
            //否則,查詢的是單個對象
                redisObj =redisMap.get(key).get(fieldKey);
            }
            if (redisObj !=null) {
                return redisObj;
            }
        }catch (Exception e) {
            Exception e2 = new Exception("查詢不到緩存異常");
            e.printStackTrace();
            e2.printStackTrace();
        }
        //以上,是使用AOP攔截查詢方法,如果緩存中存在,則直接返回緩存結果,
        //減少數(shù)據(jù)庫查詢壓力。
        //沒有緩存則讀取MySQL
        System.out.println("查詢不到緩存");
        //執(zhí)行方法
        Object resultOld = joinPoint.proceed();
        //查詢結果不為空,則存入緩存,便于下次直接從緩存中查詢數(shù)據(jù)
        if (resultOld != null) {
            try {
                //        然后將讀取的結果保存至Redis緩存
                boolean resultRow = false;
                if (fieldKey.equals("SelectString")) {
                    //保存的是集合,需要遍歷存儲
                    //先類型強轉
                    List<Object> objectList = (List<Object>) resultOld;
                    //遍歷返回值集合,進行緩存
                    for (Object o : objectList) {
                    //由于不同對象存儲緩存時,使用的key、fieldKey都不相同,
                    //本次模擬都是以數(shù)據(jù)表的主鍵值作為fieldKey來存儲,然后用不同的key作為區(qū)分。
                    //因此需要進行類型轉換來獲取每個不同對象的不同主鍵調(diào)用方法。
                    //當然,如果你所有的對象獲取主鍵的方法名都一樣的話,
                    //完全可以使用反射中的【使用方法名獲取方法】來調(diào)用對象返回主鍵值。
                        if (o instanceof Barrage) { //緩存彈幕對象
                            Barrage barrageO = (Barrage) o;
                            fieldKey = barrageO.getBaId() + "";
                            //增加單個
                            redisMap.put(key, new HashMap<>().put(fieldKey,barrageO ))
                         
                        } else if (o instanceof Video){//緩存視頻對象
                            Video videoO = (Video) o;
                            fieldKey = barrageO.getvId() + "";
                            //增加單個
                            redisMap.put(key, new HashMap<>().put(fieldKey,videoO ))
                        } else {
                            //TODO 繼續(xù)增
                        }
                    }
                } else {
                    //增加單個
                    redisTemplate.opsForHash().put(key, fieldKey, resultOld);
                }
            } catch (Exception e) {
                Exception e2 = new Exception("查詢后添加緩存異常");
                e.printStackTrace();
                e2.printStackTrace();
            }
        }
        return resultOld;
    }
        public String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod());
        Expression expression = parser.parseExpression(spELString);
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        for(int i = 0 ; i < args.length ; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        return expression.getValue(context).toString();
    }
}

4.測試

緩存視頻稿件:存儲數(shù)據(jù)格式為:

  • VideoByVSIdXXX - VideoId-Video
  • VideoByVSId+視頻稿件Id—視頻Id—視頻
 @SelectRedisCache(key = "'VideoByVSId' + #V_OriginId", fieldKey = "")
    public List<Video> findByOrigin(Long V_OriginType, Long V_OriginId) {
        List<Video> videoList = videoMapper.selectList(new EntityWrapper<Video>().eq("V_OriginType",V_OriginType).eq("V_OriginId",V_OriginId));
        for (Video video:videoList) {
            video.setBarrages(barrageService.findByVId(video.getvId()));
        }
        return videoList;
    }

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Java DriverManager.getConnection()獲取數(shù)據(jù)庫連接

    Java DriverManager.getConnection()獲取數(shù)據(jù)庫連接

    這篇文章主要介紹了Java DriverManager.getConnection()獲取數(shù)據(jù)庫連接,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • java自定義注解實現(xiàn)前后臺參數(shù)校驗的實例

    java自定義注解實現(xiàn)前后臺參數(shù)校驗的實例

    下面小編就為大家?guī)硪黄猨ava自定義注解實現(xiàn)前后臺參數(shù)校驗的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • 單例模式垃圾回收_動力節(jié)點Java學院整理

    單例模式垃圾回收_動力節(jié)點Java學院整理

    這篇文章主要為大家詳細介紹了單例模式垃圾回收的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Spring Cloud Feign實例講解學習

    Spring Cloud Feign實例講解學習

    這篇文章主要介紹了Spring Cloud Feign實例講解學習,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • 如何對jdk版本升級或降級

    如何對jdk版本升級或降級

    這篇文章主要介紹了如何對jdk版本升級或降級方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 詳解java中的6種單例寫法及優(yōu)缺點

    詳解java中的6種單例寫法及優(yōu)缺點

    在java中,單例有很多種寫法,面試時,手寫代碼環(huán)節(jié),除了寫算法題,有時候也會讓手寫單例模式,這里記錄一下單例的幾種寫法和優(yōu)缺點。需要的朋友可以參考下
    2018-11-11
  • Java ArrayDeque使用方法詳解

    Java ArrayDeque使用方法詳解

    這篇文章主要為大家詳細介紹了Java ArrayDeque的使用方法,感興趣的小伙伴們可以參考一下
    2016-03-03
  • Java數(shù)字轉換工具類NumberUtil的使用

    Java數(shù)字轉換工具類NumberUtil的使用

    NumberUtil是一個功能強大的Java工具類,用于處理數(shù)字的各種操作,包括數(shù)值運算、格式化、隨機數(shù)生成和數(shù)值判斷,下面就來介紹一下NumberUtil的具體使用,感興趣的可以了解一下
    2025-02-02
  • Springboot程序在使用Maven下載依賴時失效的解決方法

    Springboot程序在使用Maven下載依賴時失效的解決方法

    以前能成功創(chuàng)建SpringBoot項目并運行,但是現(xiàn)在再創(chuàng)建新項目時Maven下載依賴失敗,且maven依賴刷新按鍵一直沒反應,所以本文給大家介紹了Springboot程序在使用Maven下載依賴時失效的解決方法,需要的朋友可以參考下
    2024-05-05
  • java從list中取出對象并獲得其屬性值的方法

    java從list中取出對象并獲得其屬性值的方法

    這篇文章主要介紹了java從list中取出對象并獲得其屬性值的方法,大家參考使用
    2013-12-12

最新評論

呼和浩特市| 石柱| 鄱阳县| 鸡东县| 阿拉尔市| 左权县| 墨竹工卡县| 东海县| 巴里| 伊金霍洛旗| 拜泉县| 德昌县| 社会| 定远县| 潼南县| 宁陕县| 宜阳县| 福泉市| 晋州市| 盐边县| 天峻县| 收藏| 吉木萨尔县| 德安县| 台州市| 海安县| 阳江市| 濮阳市| 稻城县| 合阳县| 永和县| 庐江县| 花垣县| 广水市| 高州市| 治多县| 城步| 新田县| 塘沽区| 苗栗县| 兰西县|