使用 Spring 快取資料

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

您將構建什麼

您將構建一個應用程式,為簡單的圖書儲存庫啟用快取。

您需要什麼

如何完成本指南

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

從頭開始,請轉到從 Spring Initializr 開始

跳過基礎知識,請執行以下操作

完成後,您可以將您的結果與 gs-caching/complete 中的程式碼進行比較。

從 Spring Initializr 開始

您可以使用這個 預初始化專案 並單擊“生成”下載 ZIP 檔案。該專案已配置為符合本教程中的示例。

手動初始化專案

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

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

  3. 單擊 Dependencies 並選擇 Spring cache abstraction

  4. 單擊生成

  5. 下載生成的 ZIP 檔案,這是一個已根據您的選擇配置好的 Web 應用程式存檔。

如果您的 IDE 集成了 Spring Initializr,您可以從 IDE 中完成此過程。
您還可以從 Github fork 該專案並在您的 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 或不同 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 許可,文字內容採用署名-禁止演繹知識共享許可

獲取程式碼