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

SpringBoot使用Cache集成Redis做緩存的保姆級教程

 更新時間:2025年01月13日 09:36:27   作者:笑小楓  
Spring Cache是Spring框架提供的一個緩存抽象層,它簡化了緩存的使用和管理,Spring Cache默認使用服務(wù)器內(nèi)存,并無法控制緩存時長,查找緩存中的數(shù)據(jù)比較麻煩,本文已常用的Redis作為緩存中間件作為示例,詳細講解項目中如何使用Cache提高系統(tǒng)性能,需要的朋友可以參考下

1. 項目背景

Spring Cache是Spring框架提供的一個緩存抽象層,它簡化了緩存的使用和管理。Spring Cache默認使用服務(wù)器內(nèi)存,并無法控制緩存時長,查找緩存中的數(shù)據(jù)比較麻煩。

因此Spring Cache支持將緩存數(shù)據(jù)集成到各種緩存中間件中。本文已常用的Redis作為緩存中間件作為示例,詳細講解項目中如何使用Cache提高系統(tǒng)性能。

2. Spring Cache介紹

Spring Cache是Spring框架提供的一種緩存解決方案,基于AOP原理,實現(xiàn)了基于注解的緩存功能,只需要簡單地加一個注解就能實現(xiàn)緩存功能,對業(yè)務(wù)代碼的侵入性很小。

使用Spring Cache的方法很簡單,只需要在方法上添加注解即可實現(xiàn)將方法返回數(shù)據(jù)存入緩存,以及清理緩存等注解的使用。

2.1 主要特點

  1. 統(tǒng)一的緩存抽象:Spring Cache為應(yīng)用提供了一種統(tǒng)一的緩存抽象,可以輕松集成各種緩存提供者(如Ehcache、Redis、Caffeine等),使用統(tǒng)一的API。
  2. 注解驅(qū)動:Spring Cache通過簡單的注解配置,如@Cacheable、@CachePut@CacheEvict等,可以快速實現(xiàn)緩存功能,而無需處理底層緩存邏輯。
  3. 靈活性和擴展性:Spring Cache允許根據(jù)業(yè)務(wù)需求自定義緩存策略,如緩存的失效時間、緩存的淘汰策略等。同時,它也提供了CacheManager接口和Cache接口,可以實現(xiàn)降低對各種緩存框架的耦合。

2.2 常用注解

@EnableCaching

  • 作用:開啟Spring的緩存注解支持。
  • 使用場景:在配置類上添加此注解,以啟用Spring Cache的注解處理功能。
  • 注意:此注解本身并不提供緩存實現(xiàn),而是允許你使用@Cacheable、@CachePut@CacheEvict等注解來定義緩存行為。

@Cacheable

  • 作用:在方法執(zhí)行前檢查緩存,如果緩存中存在數(shù)據(jù)則直接返回,否則執(zhí)行方法并將結(jié)果緩存。
  • value:指定緩存的名稱(或名稱數(shù)組)。緩存名稱與CacheManager中配置的緩存對應(yīng)。
  • key:用于生成緩存鍵的表達式(可選)。如果不指定,則默認使用方法的參數(shù)值作為鍵。
  • condition:條件表達式(可選),用于決定是否執(zhí)行緩存操作。
  • unless:否定條件表達式(可選),用于在方法執(zhí)行后決定是否緩存返回值。

@Cacheable注解配置參數(shù)說明

  1. value/cacheNames

    • 用于指定緩存的名稱(或名稱數(shù)組),緩存名稱作為緩存key的前綴。這是緩存的標識符,用于在CacheManager中查找對應(yīng)的緩存。
    • valuecacheNames是互斥的,即只能指定其中一個。
  2. key

    • 用于生成緩存鍵的表達式。這個鍵用于在緩存中唯一標識存儲的值。
    • 如果不指定key,則默認使用方法的參數(shù)值(經(jīng)過某種轉(zhuǎn)換)作為鍵。
    • 可以使用Spring Expression Language(SpEL)來編寫key表達式,以實現(xiàn)動態(tài)鍵的生成。
  3. keyGenerator

    • 指定一個自定義的鍵生成器(實現(xiàn) org.springframework.cache.interceptor.KeyGenerator 接口的類),用于生成緩存的鍵。與 key 屬性互斥,二者只能選其一。
    • 如果同時指定了keykeyGenerator,則會引發(fā)異常,因為它們是互斥的。
    • 開發(fā)者可以編寫自己的KeyGenerator實現(xiàn),并將其注冊到Spring容器中,然后在@Cacheable注解中引用。
  4. cacheManager

    • CacheManager表示緩存管理器,通過緩存管理器可以設(shè)置緩存過期時間。
    • 用于指定要使用的CacheManager。這是一個可選參數(shù),通常不需要顯式指定,因為Spring會默認使用配置的CacheManager。
    • 如果系統(tǒng)中配置了多個CacheManager,則需要通過此參數(shù)指定使用哪一個。
  5. cacheResolver

    • 緩存解析器,用于解析緩存名稱并返回相應(yīng)的Cache對象。這也是一個可選參數(shù)。
    • 類似于cacheManager,如果系統(tǒng)中配置了多個緩存解析邏輯,可以通過此參數(shù)指定使用哪一個。
  6. condition

    • 條件表達式,用于決定是否執(zhí)行緩存操作。這是一個可選參數(shù)。
    • 條件表達式使用SpEL編寫,如果表達式返回true,則執(zhí)行緩存操作;否則不執(zhí)行。
  7. unless

    • 否定條件表達式,用于在方法執(zhí)行后決定是否緩存返回值。這也是一個可選參數(shù)。
    • condition類似,unless也使用SpEL編寫,但它是在方法執(zhí)行后才進行評估的。
    • 如果unless表達式返回true,則不緩存返回值;否則緩存。
  8. sync

    • 是否使用異步模式進行緩存操作。這是一個可選參數(shù),通常不需要顯式指定。
    • 在多線程環(huán)境中,如果多個線程同時請求相同的數(shù)據(jù)并觸發(fā)緩存操作,使用異步模式可以避免線程阻塞和重復(fù)計算。

@Cacheable注解的這些參數(shù)是互斥或相互關(guān)聯(lián)的,例如valuecacheNames不能同時指定,keykeyGenerator也不能同時指定。此外,cacheManagercacheResolver也是互斥的,因為它們都用于指定緩存的解析和管理方式。

對于前兩個注解的應(yīng)用:

    @Cacheable(cacheNames = "cache:cacheByKey", key = "#id")
    public Integer cacheByKey(@PathVariable("id") Integer id) throws InterruptedException {
        Thread.sleep(5000);
        log.info("執(zhí)行了cacheByKey方法" + id);
        return id;
    }

看注釋掉的那行,取緩存名稱為cache:cacheByKey,參數(shù)id的值作為key,最終緩存key為:緩存名稱+“::”+key,例如:上述代碼id為123,最終的key為:cache:cacheByKey::123

SpEL(Spring Expression Language)是一種在 Spring 框架中用于處理字符串表達式的強大工具,它可以實現(xiàn)獲取對象的屬性,調(diào)用對象的方法操作。

  • 單個緩存名稱@Cacheable(value = "myCache") 表示使用名為myCache的緩存。
  • 多個緩存名稱@Cacheable(value = {"cache1", "cache2"}) 表示方法的結(jié)果將同時緩存到cache1cache2中。
  • @CacheConfig結(jié)合使用:如果類上使用了@CacheConfig注解,并且指定了cacheNames屬性,那么類中的方法在使用@Cacheable時可以省略value屬性,直接使用類級別的緩存配置。

@CacheEvict

  • 作用:從緩存中刪除數(shù)據(jù)。
  • value:指定要刪除的緩存的名稱(或名稱數(shù)組)。
  • key:用于指定要刪除的緩存鍵(可選)。如果不指定,則默認使用方法的參數(shù)值作為鍵。
  • allEntries:布爾值,指定是否刪除緩存中的所有條目(而不是僅刪除與指定鍵匹配的條目)。
  • beforeInvocation:布爾值,指定是否在方法執(zhí)行之前刪除緩存(默認為false,即在方法執(zhí)行之后刪除)。

@CachePut

  • 作用:更新緩存中的數(shù)據(jù),無論方法是否成功執(zhí)行,都會將結(jié)果放入緩存。
  • value、key、conditionunless:與@Cacheable中的這些屬性相同。

@Caching

  • 作用:允許在同一個方法上組合使用多個緩存注解(如@Cacheable、@CachePut、@CacheEvict)。
  • 屬性:包含一個或多個緩存注解。

@CacheConfig

  • 作用:為類級別提供緩存相關(guān)的默認配置。
  • cacheNames:指定該類中所有方法使用的默認緩存名稱(或名稱數(shù)組)。
  • keyGenerator:指定自定義的鍵生成器(可選)。
  • cacheManager:指定要使用的CacheManager(可選)。

3. 示例代碼

項目依賴于Redis配置,這里就不多贅述了。

緩存管理器配置:

定義了兩個緩存管理器,默認cacheManager(使用@Primary標注),一個緩存返回值為null的管理器cacheNullManager,詳情看下面代碼。

package com.maple.redis.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
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 org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * @author 笑小楓
 * @date 2025/1/7
 */
@Slf4j
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {

    /**
     * 默認緩存管理器
     * 只有CacheManger才能掃描到cacheable注解
     * spring提供了緩存支持Cache接口,實現(xiàn)了很多個緩存類,其中包括RedisCache。但是我們需要對其進行配置,這里就是配置RedisCache
     */
    @Bean
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return RedisCacheManager.RedisCacheManagerBuilder
                //Redis鏈接工廠
                .fromConnectionFactory(redisConnectionFactory)
                //緩存配置 通用配置  默認存儲一小時
                .cacheDefaults(getCacheConfigurationWithTtl(Duration.ofHours(1)))
                //配置同步修改或刪除  put/evict
                .transactionAware()
                //對于不同的cacheName我們可以設(shè)置不同的過期時間
                .withCacheConfiguration("cache2:cacheByUser", getCacheConfigurationWithTtl(Duration.ofHours(2)))
                .build();
    }

    /**
     * 創(chuàng)建并返回一個CacheManager Bean,用于管理Redis緩存。
     * 主要返回結(jié)果為null時使用,會緩存null值,緩存時間為10分鐘,防止緩存穿透。
     * 使用時通過 cacheManager = "cacheNullManager" 指定使用該緩存管理器。
     */
    @Bean
    public CacheManager cacheNullManager(RedisConnectionFactory redisConnectionFactory) {
        return RedisCacheManager.RedisCacheManagerBuilder
                //Redis鏈接工廠
                .fromConnectionFactory(redisConnectionFactory)
                //緩存配置 通用配置  默認存儲一小時
                .cacheDefaults(RedisCacheConfiguration
                        .defaultCacheConfig()
                        // 設(shè)置key為String
                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                        // 設(shè)置value 為自動轉(zhuǎn)Json的Object
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()))
                        .entryTtl(Duration.ofMinutes(10)))
                //配置同步修改或刪除  put/evict
                .transactionAware()
                .build();
    }

    /**
     * 緩存的基本配置對象
     */
    private RedisCacheConfiguration getCacheConfigurationWithTtl(Duration duration) {
        return RedisCacheConfiguration
                .defaultCacheConfig()
                //設(shè)置key value的序列化方式
                // 設(shè)置key為String
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                // 設(shè)置value 為自動轉(zhuǎn)Json的Object
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()))
                // 不緩存null
                .disableCachingNullValues()
                // 設(shè)置緩存的過期時間
                .entryTtl(duration);
    }

    /**
     * 緩存的異常處理
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        // 異常處理,當Redis發(fā)生異常時,打印日志,但是程序正常走
        log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
            }

            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
                log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                log.error("Redis occur handleCacheClearError:", e);
            }
        };
    }

    @Override
    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuffer sb = new StringBuffer();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

使用案例:

User對象就id、name兩個字段,大家隨意~

package com.maple.redis.controller;

import com.maple.redis.bean.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.web.bind.annotation.*;

/**
 * @author 笑小楓
 * @date 2025/1/7
 */
@Slf4j
@RestController
@RequestMapping("/cache")
public class TestCacheController {

    /**
     * 獲取簡單緩存數(shù)據(jù)。
     *
     * <p>通過@Cacheable注解,該方法的結(jié)果會被緩存到名為"cache:simpleCache"的緩存中。
     * 如果在緩存中找到相同請求的結(jié)果,將直接返回緩存的值,避免重復(fù)執(zhí)行方法體中的邏輯。
     *
     * <p>方法內(nèi)部,使用Thread.sleep(5000)模擬了一個耗時操作,
     */
    @GetMapping("/simpleCache")
    @Cacheable(cacheNames = "cache:simpleCache")
    public String simpleCache() throws InterruptedException {
        Thread.sleep(5000);
        log.info("執(zhí)行了simpleCache方法");
        return "test";
    }

    /**
     * 如果緩存中存在對應(yīng)的ID,則直接從緩存中獲取結(jié)果,避免重復(fù)執(zhí)行耗時操作。
     * 如果緩存中不存在,則執(zhí)行方法體中的邏輯,將結(jié)果存入緩存并返回。
     * 方法執(zhí)行過程中,通過Thread.sleep模擬了一個耗時操作。
     */
    @GetMapping("/{id}")
    @Cacheable(cacheNames = "cache:cacheByKey", key = "#id")
    public Integer cacheByKey(@PathVariable("id") Integer id) throws InterruptedException {
        Thread.sleep(5000);
        log.info("執(zhí)行了cacheByKey方法" + id);
        return id;
    }

    /**
     * <p>該方法使用@Caching注解集成了多個緩存策略:</p>
     * <ul>
     *     <li>
     *         當方法返回值為null時(即緩存穿透情況),使用名為"cacheNullManager"的CacheManager進行緩存處理,
     *         緩存名稱為"cache2:cacheByKey",緩存鍵為傳入的用戶ID,并設(shè)置緩存過期時間為10分鐘。
     *         這通過@Cacheable注解的cacheManager屬性指定緩存管理器,unless屬性設(shè)置緩存條件(當結(jié)果為null時緩存)。
     *     </li>
     *     <li>
     *         當方法返回值不為null時,使用默認的CacheManager進行緩存處理,
     *         緩存名稱和鍵的設(shè)置與上述相同,但此時緩存管理器為默認配置。
     *         這通過另一個@Cacheable注解實現(xiàn),其unless屬性設(shè)置為當結(jié)果為null時不緩存。
     *     </li>
     * </ul>
     *
     * <p>在方法執(zhí)行過程中,通過Thread.sleep模擬了一個耗時操作。</p>
     */
    @Caching(
            cacheable = {
                    //result為null時,屬于緩存穿透情況,使用cacheNullManager緩存管理器進行緩存,并且設(shè)置過期時間為10分鐘。
                    @Cacheable(cacheNames = "cache2:cacheByKey", key = "#id", unless = "#result != null", cacheManager = "cacheNullManager"),
                    //result不為null時,使用默認緩存管理器進行緩存。
                    @Cacheable(cacheNames = "cache2:cacheByKey", key = "#id", unless = "#result == null")
            }
    )
    @GetMapping("/cacheMore/{id}")
    public User cacheMore(@PathVariable("id") Integer id) throws InterruptedException {
        Thread.sleep(5000);
        if (id > 100) {
            return null;
        } else {
            return new User(id, "zhangsan");
        }
    }

    @PostMapping("/cacheByUser")
    @Cacheable(cacheNames = "cache2:cacheByUser", key = "#user.id")
    public User cacheByUser(@RequestBody User user) throws InterruptedException {
        Thread.sleep(5000);
        log.info("執(zhí)行了cacheByUser方法" + user.getId());
        return user;
    }

    @PostMapping("/cacheByIdAndName")
    @Cacheable(cacheNames = "cache2:cacheByUser", key = "#user.id")
    public User cacheByIdAndName(@RequestBody User user) throws InterruptedException {
        Thread.sleep(5000);
        log.info("執(zhí)行了cacheByUser方法" + user.getId());
        return user;
    }

    /**
     * 根據(jù)用戶ID大于100的條件進行緩存處理。
     *
     * @param user 用戶對象,包含用戶ID等信息。
     * @return 返回傳入的用戶對象。
     * @throws InterruptedException 如果線程被中斷,則拋出此異常。
     *
     *                              通過@Cacheable注解實現(xiàn)了緩存功能,當請求的用戶ID大于100時,會觸發(fā)緩存機制。
     *                              緩存的名稱設(shè)置為"cache2:cacheByUser",緩存的鍵為傳入的用戶對象的ID。
     *                              如果緩存中已存在對應(yīng)的用戶數(shù)據(jù),則直接從緩存中獲取并返回,避免重復(fù)執(zhí)行耗時操作。
     *                              如果緩存中不存在,則執(zhí)行方法體中的邏輯,將結(jié)果存入緩存并返回。
     *                              在方法執(zhí)行過程中,通過Thread.sleep模擬了一個耗時操作。
     */
    @PostMapping("/cacheByUserIdGt100")
    @Cacheable(cacheNames = "cache2:cacheByUser", key = "#user.id", condition = "#user.id > 100")
    public User cacheByUserIdGt100(@RequestBody User user) throws InterruptedException {
        Thread.sleep(5000);
        log.info("執(zhí)行了cacheByUser方法" + user.getId());
        return user;
    }

    /**
     * 更新用戶信息。
     * <p>
     * 使用@CachePut注解將更新后的用戶信息存入緩存中。
     * 緩存的名稱設(shè)置為"cache2:cacheByUser",緩存的鍵為傳入的User對象的ID。
     * 如果緩存中已存在對應(yīng)的用戶數(shù)據(jù),則更新緩存中的值;如果不存在,則創(chuàng)建新的緩存條目。
     * 在方法執(zhí)行過程中,通過Thread.sleep模擬了一個耗時操作。
     */
    @PostMapping("/updateUser")
    @CachePut(cacheNames = "cache2:cacheByUser", key = "#user.id")
    public User updateUser(@RequestBody User user) throws InterruptedException {
        Thread.sleep(5000);
        log.info("執(zhí)行了saveUser方法" + user.getId());
        return user;
    }

    /**
     * 刪除指定ID的用戶,并從緩存中移除對應(yīng)的數(shù)據(jù)。
     * <p>
     * 使用@CacheEvict注解用于從緩存中移除指定ID的用戶數(shù)據(jù)。
     * 緩存的名稱設(shè)置為"cache2:cacheByUser",緩存的鍵為傳入的用戶ID。
     * 在執(zhí)行刪除操作前,方法通過Thread.sleep模擬了一個耗時操作。
     */
    @DeleteMapping("/{id}")
    @CacheEvict(cacheNames = "cache2:cacheByUser", key = "#id")
    public void deleteUser(@PathVariable("id") Integer id) throws InterruptedException {
        Thread.sleep(10000);
        log.info("執(zhí)行了deleteUser方法" + id);
    }
}

模擬了多種緩存使用的方式

  • updateUser使用@CachePut對數(shù)據(jù)進行緩存或更新。
  • deleteUser使用@CacheEvict刪除緩存。
  • cacheMore根據(jù)條件選擇不同的緩存管理器進行緩存數(shù)據(jù)。

簡單附幾張測試截圖吧

第一次查詢,沒有緩存截圖:

后續(xù)查詢走緩存的截圖

redis緩存數(shù)據(jù)格式:

redis緩存數(shù)據(jù)詳情:

4. SpEL在Spring Cache中的應(yīng)用

4.1 SpEL概述

SpEL是Spring框架提供的一種功能強大的表達式語言,它能夠在運行時查詢和操作對象圖。SpEL的語法簡潔,支持方法調(diào)用、字符串模板、集合操作、邏輯運算等復(fù)雜功能,使得在Spring配置和代碼中能夠更輕松地處理復(fù)雜的邏輯和數(shù)據(jù)結(jié)構(gòu)。

4.2 SpEL應(yīng)用

  1. 動態(tài)生成緩存鍵

    • 在Spring Cache中,緩存鍵(Key)用于在緩存中唯一標識數(shù)據(jù)。通過使用SpEL表達式,可以根據(jù)方法參數(shù)、返回值等動態(tài)生成緩存鍵。
    • 例如,在@Cacheable注解中,可以使用key屬性配合SpEL表達式來指定緩存鍵的生成規(guī)則。
  2. 條件緩存

    • Spring Cache允許通過condition屬性來指定緩存的條件。當條件滿足時,才會執(zhí)行緩存操作(如緩存數(shù)據(jù)或移除緩存)。
  3. 除非條件

    • unless屬性與condition屬性類似,但它用于指定不執(zhí)行緩存操作的條件。
    • 當unless條件滿足時,即使方法被調(diào)用,其結(jié)果也不會被緩存。
    • unless屬性同樣支持SpEL表達式。

4.3 SpEL表達式在Spring Cache中的常用變量

  1. #參數(shù)名

    • 表示方法參數(shù)??梢酝ㄟ^參數(shù)名來引用方法參數(shù)的值。
    • 例如,#param1表示第一個參數(shù)的值。
  2. #result

    • 表示方法的返回值。在@CachePut和@CacheEvict注解中,可以使用#result來引用方法的返回值。
  3. #root

    • 表示緩存表達式根對象(CacheExpressionRootObject)。它提供了對緩存操作上下文的訪問。
    • 通過#root,可以獲取到緩存的詳細信息,如緩存名稱、方法參數(shù)等。

注意:

condition屬性在Spring Cache中用于在方法執(zhí)行前判斷是否執(zhí)行緩存操作,并且不能引用方法的返回值;而unless屬性則用于在方法執(zhí)行后根據(jù)返回值或其他條件來決定是否緩存數(shù)據(jù)。

5. 工作原理

Spring Cache是基于AOP原理,對添加注解@Cacheable的類生成代理對象,在方法執(zhí)行前查看是否有緩存對應(yīng)的數(shù)據(jù),如果有直接返回數(shù)據(jù),如果沒有調(diào)用源方法獲取數(shù)據(jù)返回,并緩存起來,下邊跟蹤Spring Cache的切面類CacheAspectSupport.java中的private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts)方法。

@Nullable
    private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
        if (contexts.isSynchronized()) {
            CacheOperationContext context = (CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next();
            if (!this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
                return this.invokeOperation(invoker);
            }

            Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
            Cache cache = (Cache)context.getCaches().iterator().next();

            try {
                return this.wrapCacheValue(method, this.handleSynchronizedGet(invoker, key, cache));
            } catch (Cache.ValueRetrievalException var10) {
                Cache.ValueRetrievalException ex = var10;
                ReflectionUtils.rethrowRuntimeException(ex.getCause());
            }
        }

        this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT);
        Cache.ValueWrapper cacheHit = this.findCachedItem(contexts.get(CacheableOperation.class));
        List<CachePutRequest> cachePutRequests = new ArrayList();
        if (cacheHit == null) {
            this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
        }

        Object cacheValue;
        Object returnValue;
        if (cacheHit != null && !this.hasCachePut(contexts)) {//如果緩存有,則從緩存取
            cacheValue = cacheHit.get();
            returnValue = this.wrapCacheValue(method, cacheValue);
        } else {//緩存沒有,執(zhí)行原始方法
            returnValue = this.invokeOperation(invoker);
            cacheValue = this.unwrapReturnValue(returnValue);//再存緩存
        }

        this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
        Iterator var8 = cachePutRequests.iterator();

        while(var8.hasNext()) {
            CachePutRequest cachePutRequest = (CachePutRequest)var8.next();
            cachePutRequest.apply(cacheValue);
        }

        this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
        return returnValue;
    }

6.總結(jié)

以上就是SpringBoot使用Cache集成Redis做緩存的保姆級教程的詳細內(nèi)容,更多關(guān)于SpringBoot Cache集成Redis做緩存的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java對象類型的判斷詳解

    Java對象類型的判斷詳解

    這篇文章主要介紹了Java對象類型的判斷,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 關(guān)于jackson序列化和feign返回值的問題

    關(guān)于jackson序列化和feign返回值的問題

    這篇文章主要介紹了關(guān)于jackson序列化和feign返回值的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Windows安裝兩個或多個JDK并實現(xiàn)自由切換的方法

    Windows安裝兩個或多個JDK并實現(xiàn)自由切換的方法

    最近新接手一個項目,啟動的時候,發(fā)現(xiàn)有些jar和現(xiàn)在正在使用的JDK版本不一致,一直啟動有問題,想著就多裝一個JDK,由于為了保證java的運行環(huán)境和編譯環(huán)境保持一致,就需要我們設(shè)置jdk的環(huán)境變量,所以本文給大家介紹了Windows安裝兩個或多個JDK并實現(xiàn)自由切換的方法
    2025-03-03
  • SpringBoot全局配置long轉(zhuǎn)String丟失精度的問題解決

    SpringBoot全局配置long轉(zhuǎn)String丟失精度的問題解決

    web項目中,Java后端傳過來的Long/long類型,前端JS接收會丟失精度。那么應(yīng)該如何解決,本文就來介紹一下幾種方法,感興趣的可以了解一下
    2021-08-08
  • Java日常練習(xí)題,每天進步一點點(41)

    Java日常練習(xí)題,每天進步一點點(41)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-07-07
  • SpringBoot項目整合mybatis的方法步驟與實例

    SpringBoot項目整合mybatis的方法步驟與實例

    今天小編就為大家分享一篇關(guān)于SpringBoot項目整合mybatis的方法步驟與實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • WebSocket實現(xiàn)系統(tǒng)后臺消息實時通知功能

    WebSocket實現(xiàn)系統(tǒng)后臺消息實時通知功能

    在現(xiàn)代Web應(yīng)用中,提供實時通知對于改善用戶體驗至關(guān)重要,WebSocket技術(shù)允許建立雙向通信通道,從系統(tǒng)后臺將消息實時傳送給系統(tǒng)用戶,下面我們就來深入探討一下如何使用WebSocket來實現(xiàn)這一功能吧
    2023-10-10
  • SpringBoot實現(xiàn)自定義線程池的方法

    SpringBoot實現(xiàn)自定義線程池的方法

    這篇文章主要介紹了SpringBoot中的自定義線程池解析,實現(xiàn)自定義線程池重寫spring默認線程池的方式使用的時候,只需要加@Async注解就可以,不用去聲明線程池類,需要的朋友可以參考下
    2023-11-11
  • MyBatis-Plus 分頁插件配置的兩種方式實現(xiàn)

    MyBatis-Plus 分頁插件配置的兩種方式實現(xiàn)

    本文主要介紹了MyBatis-Plus 分頁插件配置的兩種方式實現(xiàn),包括使用PaginationInterceptor和MybatisPlusInterceptor兩種方式,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • mybatis實現(xiàn)動態(tài)升降序的問題小結(jié)

    mybatis實現(xiàn)動態(tài)升降序的問題小結(jié)

    文章介紹了如何在MyBatis的XML文件中實現(xiàn)動態(tài)排序,使用$符號而不是#符號來引用變量,以避免SQL注入,同時,強調(diào)了在Java代碼中進行防注入處理的重要性,感興趣的朋友一起看看吧
    2025-02-02

最新評論

谷城县| 通许县| 军事| 从江县| 华池县| 曲阳县| 绥棱县| 嘉兴市| 浠水县| 呼玛县| 元氏县| 通州市| 姜堰市| 连城县| 保定市| 北京市| 盐亭县| 辉南县| 墨玉县| 伊宁县| 鹿邑县| 嘉祥县| 长春市| 望谟县| 海安县| 遂宁市| 大港区| 香格里拉县| 洱源县| 贺州市| 石首市| 阿拉尔市| 桂东县| 靖江市| 桃园市| 牟定县| 武汉市| 万年县| 九龙县| 逊克县| 余干县|