從 0 學(xué)習(xí) Spring 緩存數(shù)據(jù)

前言

昨天在開(kāi)發(fā)業(yè)務(wù)時(shí),打算加入緩存層來(lái)提高系統(tǒng)響應(yīng)速度。查找了一些資料,發(fā)現(xiàn) Spring 的緩存功能十分強(qiáng)大!只需要添加少量的代碼,就可以輕松緩存方法所返回的對(duì)象。這篇文章通過(guò)描述一個(gè)實(shí)際使用例子,介紹 Spring Cache 的使用限制以及注意事項(xiàng)。

環(huán)境準(zhǔn)備

  • Redis 5+
  • JDK 1.8+
  • Gradle 6+
  • 一款你喜愛(ài)的 IDE

實(shí)踐過(guò)程

添加依賴

打開(kāi) build.gradle 文件,添加 Spring Cache 依賴。

implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

創(chuàng)建模型

@Data
@AllArgsConstructor
public class Post implements Serializable {

    private Long id;

    private String title;

    private String content;
}

PS:這里使用到了 Lombok 插件,如果不熟悉可先查詢相關(guān)資料進(jìn)行了解。

創(chuàng)建模型倉(cāng)庫(kù)

public interface PostRepository {

    Post getById(Long id);

}
@Component
public class PostRepositoryImpl implements PostRepository {
    @Override
    public Post getById(Long id) {
        // 模擬查詢時(shí)間
        simulateSlowService();
        return new Post(100L, "title", "content");
    }

    private void simulateSlowService() {
        try {
            Long time = 3000L;
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

編寫控制器

@RestController
public class PostController {

    private final PostRepository postRepository;

    public PostController(PostRepository postRepository) {
        this.postRepository = postRepository;
    }

    @GetMapping("posts/{id}")
    public Post getPostById(@PathVariable("id") Long id) {
        return postRepository.getById(id);
    }
}

針對(duì)一些不容易被修改的資源,如果每次都需要到持久化數(shù)據(jù)庫(kù)中進(jìn)行查詢,無(wú)疑是十分浪費(fèi)的,體驗(yàn)也差,下面我們使用 Spring Cache 來(lái)改進(jìn)一波。

使用 Spring Cache

@EnableCaching
@SpringBootApplication
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}

添加 @EnableCaching 注解啟動(dòng) Spring Cache。

spring:
  cache:
    type: redis
  redis:
    host: 127.0.0.1
    port: 6379

這里用 Redis 作為緩存引擎,如果小伙伴想用其他引擎,可自行查閱文檔進(jìn)行配置。

@RestController
public class PostController {

    private final PostRepository postRepository;

    public PostController(PostRepository postRepository) {
        this.postRepository = postRepository;
    }
    
    @Cacheable(cacheNames = "getPostById", key = "#id")
    @GetMapping("posts/{id}")
    public Post getPostById(@PathVariable("id") Long id) {
        return postRepository.getById(id);
    }
}

使用 @Cacheable 注解 getPostById 方法,使用了 cacheNames 和 key 參數(shù)。這里先不展開(kāi)說(shuō),下面會(huì)集中梳理幾種注解以及它們的參數(shù)意義。

Spring Cache 注解

Spring Cache 常用的 5 個(gè)注解,分別是:

  • @EnableCaching
  • @Cacheable
  • @CachePut
  • @CacheEvict
  • @CacheConfig

@EnableCaching

在啟動(dòng)類添加 @EnableCaching 注解讓系統(tǒng)開(kāi)啟緩存功能。

@Cacheable

功能是開(kāi)啟緩存,可以標(biāo)記在類上或者是方法上。在調(diào)用方法時(shí),會(huì)先從緩存中獲取結(jié)果,若不存在再執(zhí)行方法。主要參數(shù)包括 cacheNames、key、condition 和 unless 等。

  • cacheNames:用于指定緩存存儲(chǔ)的集合名,必須填寫。
  • key:緩存對(duì)象存儲(chǔ)在集合中的 key 值,缺省按照函數(shù)的所有參數(shù)組合作為 key 值。
  • condition:緩存對(duì)象的條件,需使用 SpEL 表達(dá)式。只有滿足表達(dá)式條件的內(nèi)容才會(huì)被緩存。
  • unless:緩存對(duì)象的條件,需使用 SpEL 表達(dá)式。它不同于 condition 參數(shù)的地方在于它的判斷時(shí)機(jī),該條件是在函數(shù)被調(diào)用之后才做判斷的,所以它可以通過(guò)對(duì)返回對(duì)象進(jìn)行判斷。
  • keyGenerator:用于指定 key 生成器。若需要自定義 key 生成器,需要實(shí)現(xiàn) KeyGenerator 接口,并使用該參數(shù)來(lái)指定。
  • cacheManager:用于指定使用哪個(gè)緩存管理器。
  • cacheResolver:用于指定使用那個(gè)緩存解析器。

@CachePut

針對(duì)方法配置,與 @Cacheable 不同的地方在于它每次都會(huì)觸發(fā)真實(shí)方法的調(diào)用。簡(jiǎn)單來(lái)說(shuō)就是更新緩存數(shù)據(jù)。主要參數(shù)和 @Cacheable 一致。

@CacheEvict

針對(duì)方法配置,用來(lái)從緩存中移除相應(yīng)數(shù)據(jù)。除了與 @Cacheable 相同的參數(shù)以外,還有 allEntries 和 beforeInvocation。

  • allEntries 非必須,默認(rèn)為 false。當(dāng)為 true 時(shí),會(huì)移除所有數(shù)據(jù)。
  • beforeInvocation 非必須,默認(rèn)為 false,會(huì)在調(diào)用方法之后移除數(shù)據(jù)。當(dāng)為 true 時(shí),會(huì)在調(diào)用方法之前移除數(shù)據(jù)。

@CacheConfig

該注解是一個(gè)類級(jí)注解,可以讓類下面的方法共享 cacheNames、keyGenerator、cacheManager 和 cacheResolver 參數(shù)。

自定義 cacheNames

這里是為了讓我們的緩存注解支持自定義 TTL 失效時(shí)間,類似下面這種效果。

// 3600 秒后緩存集合自動(dòng)過(guò)期
@Cacheable(cacheNames = "getPostById#3600", key = "#id")

為了實(shí)現(xiàn)這種效果,我們創(chuàng)建一個(gè) CustomRedisCacheManager 自定義類,如下所示。

public class CustomRedisCacheManager extends RedisCacheManager {
    public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }
    
    @Override
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {

        String[] array = StringUtils.delimitedListToStringArray(name, "#");

        name = array[0];
        
        if (array.length > 1) {
            long ttl = Long.parseLong(array[1]);
            cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
        }

        return super.createRedisCache(name, cacheConfig);
    }
}

使用自定義 CustomRedisCacheManager 配置 CacheConfig。

public class CacheConfig extends CachingConfigurerSupport {

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private Integer redisPort;

    @Value("${spring.redis.database}")
    private Integer redisDatabase;

    @Override
    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1))
                .computePrefixWith(cacheName -> "caching:" + cacheName);

        return new CustomRedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory()), defaultCacheConfig);
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setHostName(redisHost);
        configuration.setPort(redisPort);
        configuration.setDatabase(redisDatabase);
        return new LettuceConnectionFactory(configuration);
    }
}

總結(jié)

本文主要介紹了 Spring Cache 的基本使用方式和常用注解。后續(xù)文章準(zhǔn)備深入了解其底層原理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容