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

SpringCache緩存抽象之CacheManager與自定義鍵生成方式

 更新時(shí)間:2025年04月16日 08:33:59   作者:程序媛學(xué)姐  
本文將深入探討Spring Cache的核心組件CacheManager及自定義鍵生成策略,幫助開(kāi)發(fā)者掌握緩存配置與優(yōu)化技巧,從而構(gòu)建高效可靠的緩存系統(tǒng),希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

在高性能Java應(yīng)用開(kāi)發(fā)中,緩存是提升系統(tǒng)響應(yīng)速度和減輕數(shù)據(jù)庫(kù)負(fù)擔(dān)的關(guān)鍵技術(shù)。Spring Framework提供了優(yōu)雅的緩存抽象層,使開(kāi)發(fā)者能夠以聲明式方式集成各種緩存實(shí)現(xiàn)。

一、Spring Cache基礎(chǔ)架構(gòu)

1.1 緩存抽象設(shè)計(jì)理念

Spring Cache的設(shè)計(jì)遵循了Spring一貫的理念:為特定技術(shù)提供高層次抽象,降低實(shí)現(xiàn)與業(yè)務(wù)代碼的耦合度。緩存抽象層由注解驅(qū)動(dòng),支持聲明式配置,大大簡(jiǎn)化了緩存操作的代碼量。開(kāi)發(fā)者只需關(guān)注緩存策略,無(wú)需編寫重復(fù)的緩存邏輯。

這種設(shè)計(jì)使得切換不同的緩存提供商變得異常簡(jiǎn)單,增強(qiáng)了應(yīng)用的可維護(hù)性與擴(kuò)展性。

// 緩存抽象的關(guān)鍵注解示例
@Service
public class ProductService {
    
    @Cacheable(value = "products", key = "#id")
    public Product getProductById(Long id) {
        // 方法調(diào)用將被緩存,相同參數(shù)的重復(fù)調(diào)用直接返回緩存結(jié)果
        return productRepository.findById(id).orElse(null);
    }
    
    @CacheEvict(value = "products", key = "#product.id")
    public void updateProduct(Product product) {
        // 更新產(chǎn)品信息并清除對(duì)應(yīng)的緩存條目
        productRepository.save(product);
    }
    
    @CachePut(value = "products", key = "#result.id")
    public Product createProduct(Product product) {
        // 創(chuàng)建產(chǎn)品并更新緩存,同時(shí)返回結(jié)果
        return productRepository.save(product);
    }
    
    @CacheEvict(value = "products", allEntries = true)
    public void clearProductCache() {
        // 清除products緩存中的所有條目
        System.out.println("產(chǎn)品緩存已清空");
    }
}

1.2 核心組件概述

Spring Cache架構(gòu)由幾個(gè)核心組件組成,各司其職又協(xié)同工作。Cache接口定義了緩存操作的基本行為;CacheManager負(fù)責(zé)創(chuàng)建、配置和管理Cache實(shí)例;KeyGenerator負(fù)責(zé)為緩存條目生成唯一鍵;CacheResolver在運(yùn)行時(shí)決定使用哪個(gè)緩存。這些組件共同構(gòu)成了靈活強(qiáng)大的緩存框架。其中,CacheManager是連接緩存抽象與具體實(shí)現(xiàn)的橋梁,是整個(gè)架構(gòu)的核心。

// Spring Cache核心接口關(guān)系
public interface Cache {
    // 緩存的名稱,用于標(biāo)識(shí)不同的緩存
    String getName();
    
    // 底層的原生緩存,可轉(zhuǎn)換為特定實(shí)現(xiàn)
    Object getNativeCache();
    
    // 根據(jù)鍵獲取緩存值
    ValueWrapper get(Object key);
    
    // 將值存入緩存
    void put(Object key, Object value);
    
    // 從緩存中移除指定鍵的條目
    void evict(Object key);
    
    // 清除緩存中的所有條目
    void clear();
}

// CacheManager定義
public interface CacheManager {
    // 獲取指定名稱的緩存
    Cache getCache(String name);
    
    // 獲取所有緩存名稱的集合
    Collection<String> getCacheNames();
}

二、CacheManager深入解析

2.1 常用CacheManager實(shí)現(xiàn)

Spring框架提供了多種CacheManager實(shí)現(xiàn),支持不同的緩存技術(shù)。ConcurrentMapCacheManager是基于ConcurrentHashMap的簡(jiǎn)單實(shí)現(xiàn),適合開(kāi)發(fā)和測(cè)試環(huán)境;EhCacheCacheManager集成了EhCache的高級(jí)特性;RedisCacheManager則提供了與Redis分布式緩存的集成,適用于生產(chǎn)環(huán)境。根據(jù)應(yīng)用需求和性能要求,選擇合適的CacheManager至關(guān)重要。每種實(shí)現(xiàn)都有其獨(dú)特的配置方式和性能特點(diǎn)。

// 不同CacheManager的配置示例
@Configuration
@EnableCaching
public class CacheConfig {
    
    // 簡(jiǎn)單內(nèi)存緩存配置
    @Bean
    public CacheManager concurrentMapCacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        cacheManager.setCacheNames(Arrays.asList("products", "customers"));
        return cacheManager;
    }
    
    // Redis緩存配置
    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10))  // 設(shè)置緩存過(guò)期時(shí)間
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));
                
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .withCacheConfiguration("products", RedisCacheConfiguration
                        .defaultCacheConfig().entryTtl(Duration.ofMinutes(5)))
                .build();
    }
    
    // Caffeine緩存配置
    @Bean
    public CacheManager caffeineCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        
        // 為不同緩存設(shè)置不同的配置
        cacheManager.setCacheSpecification("products=maximumSize=500,expireAfterWrite=5m");
        cacheManager.setCacheSpecification("customers=maximumSize=1000,expireAfterWrite=10m");
        
        return cacheManager;
    }
}

2.2 復(fù)合緩存策略

在復(fù)雜應(yīng)用中,單一緩存策略往往無(wú)法滿足所有需求。Spring提供了CompositeCacheManager,允許組合多個(gè)CacheManager,構(gòu)建多級(jí)緩存系統(tǒng)。例如,可以組合本地緩存(Caffeine)與分布式緩存(Redis),前者提供高速訪問(wèn),后者確保集群一致性。復(fù)合策略需要合理規(guī)劃緩存數(shù)據(jù)流向和一致性維護(hù)機(jī)制,避免數(shù)據(jù)不一致問(wèn)題。

// 復(fù)合緩存管理器配置
@Bean
public CacheManager compositeCacheManager(
        CaffeineCacheManager caffeineCacheManager,
        RedisCacheManager redisCacheManager) {
    
    // 創(chuàng)建復(fù)合緩存管理器
    CompositeCacheManager compositeCacheManager = new CompositeCacheManager(
            caffeineCacheManager,
            redisCacheManager
    );
    
    // 設(shè)置回退機(jī)制,當(dāng)指定緩存不存在時(shí)創(chuàng)建默認(rèn)緩存
    compositeCacheManager.setFallbackToNoOpCache(true);
    
    return compositeCacheManager;
}

// 緩存使用策略示例
@Service
public class TieredCacheService {
    
    // 使用本地緩存,適合高頻訪問(wèn)數(shù)據(jù)
    @Cacheable(value = "localProducts", cacheManager = "caffeineCacheManager")
    public Product getProductForFrontend(Long id) {
        return productRepository.findById(id).orElse(null);
    }
    
    // 使用分布式緩存,適合集群共享數(shù)據(jù)
    @Cacheable(value = "sharedProducts", cacheManager = "redisCacheManager")
    public Product getProductForApi(Long id) {
        return productRepository.findById(id).orElse(null);
    }
    
    // 兩級(jí)緩存同步更新
    @Caching(evict = {
        @CacheEvict(value = "localProducts", key = "#product.id", cacheManager = "caffeineCacheManager"),
        @CacheEvict(value = "sharedProducts", key = "#product.id", cacheManager = "redisCacheManager")
    })
    public void updateProduct(Product product) {
        productRepository.save(product);
    }
}

三、自定義鍵生成策略

3.1 默認(rèn)鍵生成機(jī)制

Spring Cache默認(rèn)使用SimpleKeyGenerator生成緩存鍵。對(duì)于無(wú)參方法,使用SimpleKey.EMPTY作為鍵;對(duì)于單參數(shù)方法,直接使用該參數(shù)作為鍵;對(duì)于多參數(shù)方法,使用包含所有參數(shù)的SimpleKey實(shí)例。這種機(jī)制簡(jiǎn)單實(shí)用,但在復(fù)雜場(chǎng)景下可能導(dǎo)致鍵沖突或難以管理。默認(rèn)鍵生成邏輯缺乏對(duì)象屬性選擇能力,無(wú)法處理包含非緩存相關(guān)字段的復(fù)雜對(duì)象。

// 默認(rèn)鍵生成器實(shí)現(xiàn)邏輯示意
public class SimpleKeyGenerator implements KeyGenerator {
    
    @Override
    public Object generate(Object target, Method method, Object... params) {
        if (params.length == 0) {
            return SimpleKey.EMPTY;
        }
        if (params.length == 1) {
            Object param = params[0];
            if (param != null && !param.getClass().isArray()) {
                return param;
            }
        }
        return new SimpleKey(params);
    }
}

// 默認(rèn)鍵生成使用示例
@Cacheable("products") // 使用默認(rèn)鍵生成器
public Product getProduct(Long id, String region) {
    // 緩存鍵將是SimpleKey(id, region)
    return productRepository.findByIdAndRegion(id, region);
}

3.2 自定義KeyGenerator實(shí)現(xiàn)

自定義KeyGenerator可以精確控制緩存鍵的生成邏輯??梢愿鶕?jù)業(yè)務(wù)需求選擇特定字段組合、應(yīng)用哈希算法或添加前綴。例如,對(duì)于復(fù)雜查詢參數(shù),可以提取核心字段構(gòu)建鍵;對(duì)于分區(qū)數(shù)據(jù),可以添加租戶ID前綴避免沖突。自定義生成器通過(guò)@Bean注冊(cè),并在@Cacheable注解中通過(guò)keyGenerator屬性引用。

// 自定義鍵生成器實(shí)現(xiàn)
@Component("customKeyGenerator")
public class CustomKeyGenerator implements KeyGenerator {
    
    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder keyBuilder = new StringBuilder();
        
        // 添加類名和方法名前綴
        keyBuilder.append(target.getClass().getSimpleName())
                 .append(".")
                 .append(method.getName());
        
        // 處理參數(shù)
        for (Object param : params) {
            keyBuilder.append(":");
            if (param instanceof Product) {
                // 對(duì)于產(chǎn)品對(duì)象,只使用ID和名稱
                Product product = (Product) param;
                keyBuilder.append("Product[")
                         .append(product.getId())
                         .append(",")
                         .append(product.getName())
                         .append("]");
            } else {
                // 其他類型直接使用toString
                keyBuilder.append(param);
            }
        }
        
        return keyBuilder.toString();
    }
}

// 在配置類中注冊(cè)
@Bean
public KeyGenerator customKeyGenerator() {
    return new CustomKeyGenerator();
}

// 使用自定義鍵生成器
@Cacheable(value = "products", keyGenerator = "customKeyGenerator")
public List<Product> findProductsByCategory(String category, boolean includeInactive) {
    // 鍵將類似于: "ProductService.findProductsByCategory:Electronics:false"
    return productRepository.findByCategory(category, includeInactive);
}

3.3 SpEL表達(dá)式定制緩存鍵

Spring Expression Language (SpEL)提供了靈活的緩存鍵定制方式,無(wú)需創(chuàng)建額外類。通過(guò)key屬性指定表達(dá)式,可以從方法參數(shù)、返回值或上下文環(huán)境構(gòu)建鍵。SpEL支持字符串操作、條件邏輯和對(duì)象導(dǎo)航,能夠處理復(fù)雜的鍵生成需求。在多租戶系統(tǒng)中,可結(jié)合SecurityContext獲取租戶信息構(gòu)建隔離的緩存鍵。

// SpEL表達(dá)式緩存鍵示例
@Service
public class AdvancedCacheService {
    
    // 使用方法參數(shù)組合構(gòu)建鍵
    @Cacheable(value = "productSearch", key = "#category + '_' + #minPrice + '_' + #maxPrice")
    public List<Product> searchProducts(String category, Double minPrice, Double maxPrice) {
        return productRepository.search(category, minPrice, maxPrice);
    }
    
    // 使用對(duì)象屬性
    @Cacheable(value = "userProfile", key = "#user.id + '_' + #user.role")
    public UserProfile getUserProfile(User user) {
        return profileService.loadProfile(user);
    }
    
    // 使用條件表達(dá)式
    @Cacheable(value = "reports", 
               key = "#reportType + (T(java.lang.String).valueOf(#detailed ? '_detailed' : '_summary'))",
               condition = "#reportType != 'REALTIME'")  // 實(shí)時(shí)報(bào)告不緩存
    public Report generateReport(String reportType, boolean detailed) {
        return reportGenerator.create(reportType, detailed);
    }
    
    // 結(jié)合內(nèi)置對(duì)象和方法
    @Cacheable(value = "securedData", 
               key = "#root.target.getTenantPrefix() + '_' + #dataId",
               unless = "#result == null")
    public SecuredData getSecuredData(String dataId) {
        return securityRepository.findData(dataId);
    }
    
    // 輔助方法,用于SpEL表達(dá)式中
    public String getTenantPrefix() {
        return SecurityContextHolder.getContext().getAuthentication().getName() + "_tenant";
    }
}

四、實(shí)踐中的緩存設(shè)計(jì)

4.1 緩存一致性策略

緩存一致性是系統(tǒng)設(shè)計(jì)的關(guān)鍵挑戰(zhàn)。在Spring Cache中,主要通過(guò)@CacheEvict和@CachePut維護(hù)一致性。時(shí)間驅(qū)動(dòng)策略通過(guò)設(shè)置TTL控制緩存過(guò)期;事件驅(qū)動(dòng)策略在數(shù)據(jù)變更時(shí)主動(dòng)更新緩存。復(fù)雜系統(tǒng)中,可以結(jié)合消息隊(duì)列實(shí)現(xiàn)跨服務(wù)緩存同步。定期刷新關(guān)鍵緩存也是保障數(shù)據(jù)新鮮度的有效手段。不同場(chǎng)景需要權(quán)衡一致性與性能。

// 緩存一致性維護(hù)示例
@Service
public class ConsistentCacheService {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    // 讀取緩存數(shù)據(jù)
    @Cacheable(value = "productDetails", key = "#id")
    public ProductDetails getProductDetails(Long id) {
        return productDetailsRepository.findById(id).orElse(null);
    }
    
    // 更新并刷新緩存
    @Transactional
    public ProductDetails updateProductDetails(ProductDetails details) {
        // 先保存數(shù)據(jù)
        ProductDetails saved = productDetailsRepository.save(details);
        
        // 發(fā)布緩存更新事件
        eventPublisher.publishEvent(new ProductCacheInvalidationEvent(saved.getId()));
        
        return saved;
    }
    
    // 事件監(jiān)聽(tīng)器,處理緩存刷新
    @EventListener
    public void handleProductCacheInvalidation(ProductCacheInvalidationEvent event) {
        clearProductCache(event.getProductId());
    }
    
    // 清除特定產(chǎn)品緩存
    @CacheEvict(value = "productDetails", key = "#id")
    public void clearProductCache(Long id) {
        // 方法體可以為空,注解處理緩存清除
        System.out.println("產(chǎn)品緩存已清除: " + id);
    }
    
    // 緩存事件定義
    public static class ProductCacheInvalidationEvent {
        private final Long productId;
        
        public ProductCacheInvalidationEvent(Long productId) {
            this.productId = productId;
        }
        
        public Long getProductId() {
            return productId;
        }
    }
}

總結(jié)

Spring Cache抽象層通過(guò)統(tǒng)一接口和聲明式注解,為Java應(yīng)用提供了強(qiáng)大而靈活的緩存支持。CacheManager作為核心組件,連接緩存抽象與具體實(shí)現(xiàn),支持從簡(jiǎn)單內(nèi)存緩存到復(fù)雜分布式緩存的各種場(chǎng)景。

自定義鍵生成策略,無(wú)論是通過(guò)KeyGenerator實(shí)現(xiàn)還是SpEL表達(dá)式定制,都為精確控制緩存行為提供了有力工具。

在實(shí)際應(yīng)用中,合理選擇CacheManager、設(shè)計(jì)緩存鍵策略并維護(hù)緩存一致性,是構(gòu)建高性能緩存系統(tǒng)的關(guān)鍵。

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

相關(guān)文章

  • kafka監(jiān)聽(tīng)問(wèn)題的解決和剖析

    kafka監(jiān)聽(tīng)問(wèn)題的解決和剖析

    這篇文章主要給大家介紹了關(guān)于kafka監(jiān)聽(tīng)問(wèn)題的解決和剖析的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java?網(wǎng)絡(luò)編程TCP、UDP、HTTP、WebSocket全解

    Java?網(wǎng)絡(luò)編程TCP、UDP、HTTP、WebSocket全解

    文章主要講解了Java網(wǎng)絡(luò)編程的核心概念及實(shí)現(xiàn)方法,涵蓋了TCP和UDP傳輸層協(xié)議、HTTP和WebSocket應(yīng)用層協(xié)議,以及生產(chǎn)環(huán)境中最佳實(shí)踐,感興趣的朋友跟隨小編一起看看吧
    2026-04-04
  • 自定義log4j日志文件命名規(guī)則說(shuō)明

    自定義log4j日志文件命名規(guī)則說(shuō)明

    這篇文章主要介紹了自定義log4j日志文件命名規(guī)則說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java編寫實(shí)現(xiàn)登陸窗口

    Java編寫實(shí)現(xiàn)登陸窗口

    這篇文章主要為大家詳細(xì)介紹了Java編寫實(shí)現(xiàn)登陸窗口,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 使用Feign傳遞請(qǐng)求頭信息(Finchley版本)

    使用Feign傳遞請(qǐng)求頭信息(Finchley版本)

    這篇文章主要介紹了使用Feign傳遞請(qǐng)求頭信息(Finchley版本),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot使用FreeMarker模板發(fā)送郵件

    SpringBoot使用FreeMarker模板發(fā)送郵件

    這篇文章主要為大家詳細(xì)介紹了SpringBoot使用FreeMarker模板發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • Java中的LinkedHashMap源碼分析

    Java中的LinkedHashMap源碼分析

    這篇文章主要介紹了Java中的LinkedHashMap源碼分析,LinkedHashMap是HashMap的子類,所以基本的操作與hashmap類似,不過(guò)呢,在插入、刪除、替換key-value對(duì)的時(shí)候,需要的朋友可以參考下
    2023-12-12
  • jdbc與druid連接池的使用詳解

    jdbc與druid連接池的使用詳解

    這篇文章主要介紹了jdbc與druid連接池的使用詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Java利用Dijkstra和Floyd分別求取圖的最短路徑

    Java利用Dijkstra和Floyd分別求取圖的最短路徑

    本文主要介紹了圖的最短路徑的概念,并分別利用Dijkstra算法和Floyd算法求取最短路徑,最后提供了基于鄰接矩陣和鄰接表的圖對(duì)兩種算法的Java實(shí)現(xiàn)。需要的可以參考一下
    2022-01-01
  • Java詳解多線程協(xié)作作業(yè)之信號(hào)同步

    Java詳解多線程協(xié)作作業(yè)之信號(hào)同步

    信號(hào)量同步是指在不同線程之間,通過(guò)傳遞同步信號(hào)量來(lái)協(xié)調(diào)線程執(zhí)行的先后次序。CountDownLatch是基于時(shí)間維度的Semaphore則是基于信號(hào)維度的
    2022-05-05

最新評(píng)論

东乡县| 万年县| 青阳县| 论坛| 富宁县| 荥经县| 连州市| 呼伦贝尔市| 乃东县| 芮城县| 南陵县| 盖州市| 将乐县| 固阳县| 五家渠市| 朔州市| 大名县| 丽江市| 盐亭县| 浙江省| 万宁市| 惠安县| 鱼台县| 萨迦县| 兴化市| 西充县| 高陵县| 铜山县| 永嘉县| 建德市| 泰州市| 彭水| 莱州市| 海林市| 淳化县| 洛扎县| 永福县| 乌什县| 玉树县| 延川县| 界首市|