亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

1. 項目背景

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

因此Spring Cache支持將緩存數(shù)據(jù)集成到各種緩存中間件中。本文已常用的Redis作為緩存中間件作為示例,詳細(xì)講解項目中如何使用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:用于生成緩存鍵的表達(dá)式(可選)。如果不指定,則默認(rèn)使用方法的參數(shù)值作為鍵。
  • condition:條件表達(dá)式(可選),用于決定是否執(zhí)行緩存操作。
  • unless:否定條件表達(dá)式(可選),用于在方法執(zhí)行后決定是否緩存返回值。

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

  1. value/cacheNames

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

    • 用于生成緩存鍵的表達(dá)式。這個鍵用于在緩存中唯一標(biāo)識存儲的值。
    • 如果不指定key,則默認(rèn)使用方法的參數(shù)值(經(jīng)過某種轉(zhuǎn)換)作為鍵。
    • 可以使用Spring Expression Language(SpEL)來編寫key表達(dá)式,以實現(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會默認(rèn)使用配置的CacheManager。
    • 如果系統(tǒng)中配置了多個CacheManager,則需要通過此參數(shù)指定使用哪一個。
  5. cacheResolver

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

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

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

    • 是否使用異步模式進(jìn)行緩存操作。這是一個可選參數(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 框架中用于處理字符串表達(dá)式的強大工具,它可以實現(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:用于指定要刪除的緩存鍵(可選)。如果不指定,則默認(rèn)使用方法的參數(shù)值作為鍵。
  • allEntries:布爾值,指定是否刪除緩存中的所有條目(而不是僅刪除與指定鍵匹配的條目)。
  • beforeInvocation:布爾值,指定是否在方法執(zhí)行之前刪除緩存(默認(rèn)為false,即在方法執(zhí)行之后刪除)。

@CachePut

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

@Caching

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

@CacheConfig

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

3. 示例代碼

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

緩存管理器配置:

定義了兩個緩存管理器,默認(rèn)cacheManager(使用@Primary標(biāo)注),一個緩存返回值為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 {

    /**
     * 默認(rèn)緩存管理器
     * 只有CacheManger才能掃描到cacheable注解
     * spring提供了緩存支持Cache接口,實現(xiàn)了很多個緩存類,其中包括RedisCache。但是我們需要對其進(jìn)行配置,這里就是配置RedisCache
     */
    @Bean
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return RedisCacheManager.RedisCacheManagerBuilder
                //Redis鏈接工廠
                .fromConnectionFactory(redisConnectionFactory)
                //緩存配置 通用配置  默認(rèn)存儲一小時
                .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)
                //緩存配置 通用配置  默認(rèn)存儲一小時
                .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() {
        // 異常處理,當(dāng)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>
     *         當(dāng)方法返回值為null時(即緩存穿透情況),使用名為"cacheNullManager"的CacheManager進(jìn)行緩存處理,
     *         緩存名稱為"cache2:cacheByKey",緩存鍵為傳入的用戶ID,并設(shè)置緩存過期時間為10分鐘。
     *         這通過@Cacheable注解的cacheManager屬性指定緩存管理器,unless屬性設(shè)置緩存條件(當(dāng)結(jié)果為null時緩存)。
     *     </li>
     *     <li>
     *         當(dāng)方法返回值不為null時,使用默認(rèn)的CacheManager進(jìn)行緩存處理,
     *         緩存名稱和鍵的設(shè)置與上述相同,但此時緩存管理器為默認(rèn)配置。
     *         這通過另一個@Cacheable注解實現(xiàn),其unless屬性設(shè)置為當(dāng)結(jié)果為null時不緩存。
     *     </li>
     * </ul>
     *
     * <p>在方法執(zhí)行過程中,通過Thread.sleep模擬了一個耗時操作。</p>
     */
    @Caching(
            cacheable = {
                    //result為null時,屬于緩存穿透情況,使用cacheNullManager緩存管理器進(jìn)行緩存,并且設(shè)置過期時間為10分鐘。
                    @Cacheable(cacheNames = "cache2:cacheByKey", key = "#id", unless = "#result != null", cacheManager = "cacheNullManager"),
                    //result不為null時,使用默認(rèn)緩存管理器進(jìn)行緩存。
                    @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的條件進(jìn)行緩存處理。
     *
     * @param user 用戶對象,包含用戶ID等信息。
     * @return 返回傳入的用戶對象。
     * @throws InterruptedException 如果線程被中斷,則拋出此異常。
     *
     *                              通過@Cacheable注解實現(xiàn)了緩存功能,當(dāng)請求的用戶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ù)進(jìn)行緩存或更新。
  • deleteUser使用@CacheEvict刪除緩存。
  • cacheMore根據(jù)條件選擇不同的緩存管理器進(jìn)行緩存數(shù)據(jù)。

簡單附幾張測試截圖吧

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

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

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

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

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

4.1 SpEL概述

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

4.2 SpEL應(yīng)用

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

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

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

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

4.3 SpEL表達(dá)式在Spring Cache中的常用變量

  1. #參數(shù)名

    • 表示方法參數(shù)。可以通過參數(shù)名來引用方法參數(shù)的值。
    • 例如,#param1表示第一個參數(shù)的值。
  2. #result

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

    • 表示緩存表達(dá)式根對象(CacheExpressionRootObject)。它提供了對緩存操作上下文的訪問。
    • 通過#root,可以獲取到緩存的詳細(xì)信息,如緩存名稱、方法參數(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做緩存的保姆級教程的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Cache集成Redis做緩存的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java之Maven工程打包jar

    Java之Maven工程打包jar

    Maven打包一般可以生成兩種包一種是可以直接運行的包,一種是依賴包(只是編譯包)。Maven默認(rèn)打包時jar,如果需要修改其他類型,可以修改pom.xml。感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • Java運算符從見過到掌握上

    Java運算符從見過到掌握上

    計算機的最基本用途之一就是執(zhí)行數(shù)學(xué)運算,作為一門計算機語言,Java也提供了一套豐富的運算符來操縱變量,本篇對大家的學(xué)習(xí)或工作具有一定的價值,需要的朋友可以參考下
    2021-09-09
  • java繼承學(xué)習(xí)之super的用法解析

    java繼承學(xué)習(xí)之super的用法解析

    本文介紹java繼承super的用法,Java繼承是會用已存在的類的定義作為基礎(chǔ)建立新類的技術(shù)新類的定義可以增加新的數(shù)據(jù)或者新的功能,也可以使用父類的功能,但不能選擇性的繼承父類 這種繼承使得復(fù)用以前的代碼非常容易,能夠大大的縮短開發(fā)的周期,需要的朋友可以參考下
    2022-02-02
  • 詳解IDEA啟動多個微服務(wù)的配置方法

    詳解IDEA啟動多個微服務(wù)的配置方法

    這篇文章主要介紹了詳解IDEA啟動多個微服務(wù)的配置方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • springboot jta atomikos實現(xiàn)分布式事物管理

    springboot jta atomikos實現(xiàn)分布式事物管理

    這篇文章主要介紹了springboot jta atomikos實現(xiàn)分布式事物管理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • 一文帶你深入了解Java?String的不可變性

    一文帶你深入了解Java?String的不可變性

    這篇文章主要來和大家一起深入探討一下Java?String中的不可變性,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-06-06
  • 淺談SpringBoot實現(xiàn)自動裝配的方法原理

    淺談SpringBoot實現(xiàn)自動裝配的方法原理

    SpringBoot的自動裝配是它的一大特點,可以大大提高開發(fā)效率,減少重復(fù)性代碼的編寫。本文將詳細(xì)講解SpringBoot如何實現(xiàn)自動裝配,需要的朋友可以參考下
    2023-05-05
  • SpringBoot中Bean拷貝及工具類封裝的實現(xiàn)

    SpringBoot中Bean拷貝及工具類封裝的實現(xiàn)

    本文主要介紹了SpringBoot中Bean拷貝及工具類封裝的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 多種情況下jar包獲取文件的路徑,讀取文件方式

    多種情況下jar包獲取文件的路徑,讀取文件方式

    文章介紹了在不同情況下(IDEA運行和JAR包運行)獲取文件路徑的方法,并總結(jié)了每種方式的適用場景
    2024-11-11
  • selenium4.0版本在springboot中的使用問題的坑

    selenium4.0版本在springboot中的使用問題的坑

    本文主要介紹了selenium4.0版本在springboot中的使用問題的坑,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評論