使用 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 分叉專案並在您的 IDE 或其他編輯器中開啟它。

建立書籍模型

首先,您需要為您的書籍建立一個簡單的模型。 以下清單 (來自 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 + '\'' + '}';
  }

}

建立書籍儲存庫

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

package com.example.caching;

public interface BookRepository {

  Book getByIsbn(String isbn);

}

您可以使用 {SpringData}[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 開始根據類別路徑設定、其他 Bean 和各種屬性設定新增 Bean。 例如,如果 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 授權發布用於程式碼,並以 Attribution, NoDerivatives creative commons 授權 發布用於寫作。

取得程式碼