使用 Spring 快取資料

本指南將引導您完成在 Spring 管理的 bean 上啟用快取的過程。

您將構建什麼

您將構建一個在簡單書籍倉庫上啟用快取的應用。

您需要什麼

如何完成本指南

與大多數 Spring 入門指南類似,您可以從零開始完成每個步驟,也可以跳過您已經熟悉的基本設定步驟。無論哪種方式,您最終都會得到可工作的程式碼。

從零開始,請繼續閱讀從 Spring Initializr 開始

跳過基礎部分,請執行以下操作

完成後,您可以根據 gs-caching/complete 中的程式碼檢查您的結果。

從 Spring Initializr 開始

您可以使用這個預初始化的專案,然後點選 Generate 下載 ZIP 檔案。該專案配置適用於本教程中的示例。

手動初始化專案

  1. 導航到 https://start.spring.io。此服務會拉取應用所需的所有依賴項,併為您完成大部分設定。

  2. 選擇 Gradle 或 Maven 以及您想使用的語言。本指南假設您選擇了 Java。

  3. 點選 Dependencies,然後選擇 Spring cache abstraction

  4. 點選 Generate

  5. 下載生成的 ZIP 檔案,它是一個根據您的選擇配置好的 Web 應用壓縮包。

如果您的 IDE 集成了 Spring Initializr,您可以直接在 IDE 中完成此過程。
您也可以從 Github fork 該專案,然後在您的 IDE 或其他編輯器中開啟它。

建立 Book 模型

首先,您需要為您的書建立一個簡單的模型。以下清單(來自 src/main/java/com/example/caching/Book.java)展示瞭如何實現

package com.example.caching;

public class Book {

  private String isbn;
  private String title;

  public Book(String isbn, String title) {
    this.isbn = isbn;
    this.title = title;
  }

  public String getIsbn() {
    return isbn;
  }

  public void setIsbn(String isbn) {
    this.isbn = isbn;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  @Override
  public String toString() {
    return "Book{" + "isbn='" + isbn + '\'' + ", title='" + title + '\'' + '}';
  }

}

建立 Book 倉庫

您還需要一個該模型的倉庫。以下清單(來自 src/main/java/com/example/caching/BookRepository.java)展示了這樣的倉庫

package com.example.caching;

public interface BookRepository {

  Book getByIsbn(String isbn);

}

您可以使用 Spring Data 為您的倉庫提供基於各種 SQL 或 NoSQL 儲存的實現。然而,出於本指南的目的,您將只使用一個簡單的實現來模擬一些延遲(網路服務、慢速延遲或其他問題)。以下清單(來自 src/main/java/com/example/caching/SimpleBookRepository.java)展示了這樣的倉庫

package com.example.caching;

import org.springframework.stereotype.Component;

@Component
public class SimpleBookRepository implements BookRepository {

  @Override
  public Book getByIsbn(String isbn) {
    simulateSlowService();
    return new Book(isbn, "Some book");
  }

  // Don't do this at home
  private void simulateSlowService() {
    try {
      long time = 3000L;
      Thread.sleep(time);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }
  }

}

simulateSlowService 方法故意在每次呼叫 getByIsbn 時插入三秒延遲。稍後,您將透過快取來加速此示例。

使用倉庫

接下來,您需要連線倉庫並使用它來訪問一些書籍。以下清單(來自 src/main/java/com/example/caching/CachingApplication.java)展示瞭如何實現

package com.example.caching;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CachingApplication {

  public static void main(String[] args) {
    SpringApplication.run(CachingApplication.class, args);
  }

}

@SpringBootApplication 是一個方便的註解,它添加了以下所有內容

  • @Configuration:將類標記為應用上下文的 bean 定義源。

  • @EnableAutoConfiguration:告訴 Spring Boot 根據 classpath 設定、其他 bean 和各種屬性設定開始新增 bean。例如,如果 classpath 中有 spring-webmvc,則此註解會將應用標記為 Web 應用並激活關鍵行為,例如設定 DispatcherServlet

  • @ComponentScan:告訴 Spring 在 com/example 包中查詢其他元件、配置和服務,從而找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法啟動應用。您注意到沒有一行 XML 嗎?也沒有 web.xml 檔案。這個 Web 應用是 100% 純 Java 的,您無需處理任何底層或基礎設施的配置。

您還需要一個 CommandLineRunner,它注入 BookRepository 並使用不同的引數多次呼叫它。以下清單(來自 src/main/java/com/example/caching/AppRunner.java)展示了該類

package com.example.caching;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements CommandLineRunner {

  private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);

  private final BookRepository bookRepository;

  public AppRunner(BookRepository bookRepository) {
    this.bookRepository = bookRepository;
  }

  @Override
  public void run(String... args) throws Exception {
    logger.info(".... Fetching books");
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
    logger.info("isbn-4567 -->" + bookRepository.getByIsbn("isbn-4567"));
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
    logger.info("isbn-4567 -->" + bookRepository.getByIsbn("isbn-4567"));
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
  }

}

如果您此時嘗試執行該應用,您應該會注意到它相當慢,即使您多次檢索完全相同的書。以下示例輸出顯示了我們的(故意寫的糟糕的)程式碼造成的三秒延遲

2014-06-05 12:15:35.783  ... : .... Fetching books
2014-06-05 12:15:40.783  ... : isbn-1234 -->Book{isbn='isbn-1234', title='Some book'}
2014-06-05 12:15:43.784  ... : isbn-1234 -->Book{isbn='isbn-1234', title='Some book'}
2014-06-05 12:15:46.786  ... : isbn-1234 -->Book{isbn='isbn-1234', title='Some book'}

我們可以透過啟用快取來改善這種情況。

啟用快取

現在您可以在 SimpleBookRepository 上啟用快取,以便將書籍快取在 books 快取中。以下清單(來自 src/main/java/com/example/caching/SimpleBookRepository.java)展示了倉庫定義

package com.example.caching;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class SimpleBookRepository implements BookRepository {

  @Override
  @Cacheable("books")
  public Book getByIsbn(String isbn) {
    simulateSlowService();
    return new Book(isbn, "Some book");
  }

  // Don't do this at home
  private void simulateSlowService() {
    try {
      long time = 3000L;
      Thread.sleep(time);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }
  }

}

現在您需要啟用快取註解的處理,如下例(來自 src/main/java/com/example/caching/CachingApplication.java)所示

package com.example.caching;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class CachingApplication {

  public static void main(String[] args) {
    SpringApplication.run(CachingApplication.class, args);
  }

}

透過 @EnableCaching 註解會觸發一個後置處理器,該處理器檢查每個 Spring bean 的公共方法是否存在快取註解。如果找到此類註解,會自動建立一個代理來攔截方法呼叫並相應地處理快取行為。

後置處理器會處理 @Cacheable@CachePut@CacheEvict 註解。您可以查閱 Javadoc 和 參考指南瞭解更多詳情。

Spring Boot 會自動配置合適的 CacheManager 作為相關快取的提供者。更多詳情請參閱 Spring Boot 文件

我們的示例沒有使用特定的快取庫,所以我們的快取儲存是使用 ConcurrentHashMap 的簡單回退方案。快取抽象支援多種快取庫,並且完全相容 JSR-107 (JCache)。

構建可執行 JAR

您可以使用 Gradle 或 Maven 從命令列執行應用。您也可以構建一個包含所有必要依賴項、類和資源的可執行 JAR 檔案並執行它。構建可執行 JAR 可以輕鬆地在整個開發生命週期中、跨不同環境等情況下,將服務作為應用進行交付、版本控制和部署。

如果您使用 Gradle,可以使用 ./gradlew bootRun 執行應用。或者,您可以使用 ./gradlew build 構建 JAR 檔案,然後按如下方式執行 JAR 檔案

java -jar build/libs/gs-caching-0.1.0.jar

如果您使用 Maven,可以使用 ./mvnw spring-boot:run 執行應用。或者,您可以使用 ./mvnw clean package 構建 JAR 檔案,然後按如下方式執行 JAR 檔案

java -jar target/gs-caching-0.1.0.jar
此處描述的步驟建立了一個可執行的 JAR。您還可以構建一個經典的 WAR 檔案

測試應用

現在快取已啟用,您可以再次執行應用,透過新增使用或不使用相同 ISBN 的額外呼叫來檢視差異。這應該會產生巨大影響。以下清單顯示了啟用快取後的輸出

2016-09-01 11:12:47.033  .. : .... Fetching books
2016-09-01 11:12:50.039  .. : isbn-1234 -->Book{isbn='isbn-1234', title='Some book'}
2016-09-01 11:12:53.044  .. : isbn-4567 -->Book{isbn='isbn-4567', title='Some book'}
2016-09-01 11:12:53.045  .. : isbn-1234 -->Book{isbn='isbn-1234', title='Some book'}
2016-09-01 11:12:53.045  .. : isbn-4567 -->Book{isbn='isbn-4567', title='Some book'}
2016-09-01 11:12:53.045  .. : isbn-1234 -->Book{isbn='isbn-1234', title='Some book'}
2016-09-01 11:12:53.045  .. : isbn-1234 -->Book{isbn='isbn-1234', title='Some book'}

在前面的示例輸出中,第一次檢索書籍仍然需要三秒鐘。然而,對於同一本書的第二次和後續檢索則快得多,這表明快取正在發揮作用。

總結

恭喜!您剛剛在 Spring 管理的 bean 上啟用了快取。

另請參閱

以下指南可能也會有所幫助

想撰寫新指南或為現有指南貢獻力量?請檢視我們的貢獻指南

所有指南的程式碼均以 ASLv2 許可證釋出,文字內容則以 署名-禁止演繹創作共用許可證 釋出。

獲取程式碼