從 0 學習 Spring 緩存數據

前言

昨天在開發業務時,打算加入緩存層來提升系統響應速度。查找了一些資料,發現 Spring 的緩存功能十分強大!只須要添加少許的代碼,就能夠輕鬆緩存方法所返回的對象。這篇文章經過描述一個實際使用例子,介紹 Spring Cache 的使用限制以及注意事項。redis

環境準備

  • Redis 5+
  • JDK 1.8+
  • Gradle 6+
  • 一款你喜好的 IDE

實踐過程

添加依賴

打開 build.gradle 文件,添加 Spring Cache 依賴。spring

implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
複製代碼

建立模型

@Data
@AllArgsConstructor
public class Post implements Serializable {

    private Long id;

    private String title;

    private String content;
}
複製代碼

PS:這裏使用到了 Lombok 插件,若是不熟悉可先查詢相關資料進行了解。數據庫

建立模型倉庫

public interface PostRepository {

    Post getById(Long id);

}
複製代碼
@Component
public class PostRepositoryImpl implements PostRepository {
    @Override
    public Post getById(Long id) {
        // 模擬查詢時間
        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);
    }
}
複製代碼

針對一些不容易被修改的資源,若是每次都須要到持久化數據庫中進行查詢,無疑是十分浪費的,體驗也差,下面咱們使用 Spring Cache 來改進一波。數組

使用 Spring Cache

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

添加 @EnableCaching 註解啓動 Spring Cache。緩存

spring:
  cache:
    type: redis
  redis:
    host: 127.0.0.1
    port: 6379
複製代碼

這裏用 Redis 做爲緩存引擎,若是小夥伴想用其餘引擎,可自行查閱文檔進行配置。markdown

@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 參數。這裏先不展開說,下面會集中梳理幾種註解以及它們的參數意義。app

Spring Cache 註解

Spring Cache 經常使用的 5 個註解,分別是:less

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

@EnableCaching

在啓動類添加 @EnableCaching 註解讓系統開啓緩存功能。ide

@Cacheable

功能是開啓緩存,能夠標記在類上或者是方法上。在調用方法時,會先從緩存中獲取結果,若不存在再執行方法。主要參數包括 cacheNames、key、condition 和 unless 等。函數

  • cacheNames:用於指定緩存存儲的集合名,必須填寫。
  • key:緩存對象存儲在集合中的 key 值,缺省按照函數的全部參數組合做爲 key 值。
  • condition:緩存對象的條件,需使用 SpEL 表達式。只有知足表達式條件的內容纔會被緩存。
  • unless:緩存對象的條件,需使用 SpEL 表達式。它不一樣於 condition 參數的地方在於它的判斷時機,該條件是在函數被調用以後才作判斷的,因此它能夠經過對返回對象進行判斷。
  • keyGenerator:用於指定 key 生成器。若須要自定義 key 生成器,須要實現 KeyGenerator 接口,並使用該參數來指定。
  • cacheManager:用於指定使用哪一個緩存管理器。
  • cacheResolver:用於指定使用那個緩存解析器。

@CachePut

針對方法配置,與 @Cacheable 不一樣的地方在於它每次都會觸發真實方法的調用。簡單來講就是更新緩存數據。主要參數和 @Cacheable 一致。

@CacheEvict

針對方法配置,用來從緩存中移除相應數據。除了與 @Cacheable 相同的參數之外,還有 allEntries 和 beforeInvocation。

  • allEntries 非必須,默認爲 false。當爲 true 時,會移除全部數據。
  • beforeInvocation 非必須,默認爲 false,會在調用方法以後移除數據。當爲 true 時,會在調用方法以前移除數據。

@CacheConfig

該註解是一個類級註解,可讓類下面的方法共享 cacheNames、keyGenerator、cacheManager 和 cacheResolver 參數。

自定義 cacheNames

這裏是爲了讓咱們的緩存註解支持自定義 TTL 失效時間,相似下面這種效果。

// 3600 秒後緩存集合自動過時
@Cacheable(cacheNames = "getPostById#3600", key = "#id")
複製代碼

爲了實現這種效果,咱們建立一個 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);
    }
}
複製代碼

總結

本文主要介紹了 Spring Cache 的基本使用方式和經常使用註解。後續文章準備深刻了解其底層原理。

相關文章
相關標籤/搜索