知識庫 / Spring / Spring Boot RSS 訂閱

禁用 Spring Boot 中的 @Cacheable

Spring Boot
HongKong
9
11:15 AM · Dec 06 ,2025

1. 引言

緩存是一種有效的策略,通過避免在結果未改變(在已知時間內,本質上相同)時重複執行邏輯來提高性能。

Spring Boot 提供 @Cacheable 註解,我們將其定義在方法上,該註解會緩存方法的執行結果。 在某些場景下,例如在較低環境中的測試中,我們可能需要禁用緩存以觀察某些修改後的行為。

在本文中,我們將配置 Spring Boot 中的緩存,並學習在需要時如何禁用緩存。

2. 緩存設置

讓我們設置一個簡單的查詢圖書評論(按 ISBN)並使用 <em @Cacheable</em> 在某些邏輯中緩存的方法用例。

我們的實體類將是 <em BookReview</em> 類,該類包含 <em rating</em><em isbn</em> 等:

@Entity
@Table(name="BOOK_REVIEWS")
public class BookReview {
    @Id
    @GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "book_reviews_reviews_id_seq")
    @SequenceGenerator(name = "book_reviews_reviews_id_seq", sequenceName = "book_reviews_reviews_id_seq", allocationSize = 1)
    private Long reviewsId;
    private String userId;
    private String isbn;
    private String bookRating;
   
    // getters & setters
}

我們添加了一個簡單的 findByIsbn() 方法到 BookRepository 中,用於通過 isbn 查詢圖書評論:

public interface BookRepository extends JpaRepository<BookReview, Long> {
    List<BookReview> findByIsbn(String isbn);
}

BookReviewsLogic 類包含一個調用 findByIsbn() 方法的方法,該方法在 BookRepository 中調用。我們添加了 @Cacheable 註解,該註解會將給定 ISBN 的結果緩存在 book_reviews 緩存中:

@Service
public class BookReviewsLogic {
    @Autowired
    private BookRepository bookRepository;

    @Cacheable(value = "book_reviews", key = "#isbn")
    public List<BookReview> getBooksByIsbn(String isbn){
        return bookRepository.findByIsbn(isbn);
    }
}

由於我們使用了 @Cacheable 在邏輯類中,因此我們需要配置我們的緩存。我們可以通過使用帶有 @Configuration@EnableCaching 的配置類來設置緩存配置。 這裏,我們返回一個 HashMap 作為我們的緩存存儲。

@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager();
    }
}

我們已經完成了緩存設置。如果我們在 BookReviewsLogic 中執行 getBooksByIsbn(),則結果在第一次執行時會被緩存,並且後續不再重新計算(例如,查詢數據庫),從而提高性能。

讓我們編寫一個簡單的測試來驗證這一點:

@Test
public void givenCacheEnabled_whenLogicExecuted2ndTime_thenItDoesntQueriesDB(CapturedOutput output){
    BookReview bookReview = insertBookReview();

    String target = "Hibernate: select bookreview0_.reviews_id as reviews_1_0_, "
      + "bookreview0_.book_rating as book_rat2_0_, "
      + "bookreview0_.isbn as isbn3_0_, "
      + "bookreview0_.user_id as user_id4_0_ "
      + "from book_reviews bookreview0_ "
      + "where bookreview0_.isbn=?";

    // 1st execution
    bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn());
    String[] logs = output.toString()
      .split("\\r?\\n");
    assertThat(logs).anyMatch(e -> e.contains(target));

    // 2nd execution
    bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn());
    logs = output.toString()
      .split("\\r?\\n");

    long count = Arrays.stream(logs)
      .filter(e -> e.equals(target))
      .count();

    // count 1 means the select query log from 1st execution.
    assertEquals(1,count);
}

在上述測試中,我們執行了 getBooksByIsbn() 兩次,捕獲日誌,並確認由於 getBooksByIsbn() 方法在第二次執行時返回緩存結果,因此 select 查詢僅執行了一次。

為了生成針對數據庫執行的查詢的 SQL 日誌,可以在 application.properties 文件中設置以下屬性:

spring.jpa.show-sql=true

3. 關閉緩存

要關閉緩存,我們將使用 appconfig.cache.enabled 額外自定義屬性,在 application.properties 文件中

appconfig.cache.enabled=true

之後,我們可以在緩存配置文件中讀取此配置,並進行條件檢查:

@Bean
public CacheManager cacheManager(@Value("${appconfig.cache.enabled}") String isCacheEnabled) {
    if (isCacheEnabled.equalsIgnoreCase("false")) {
        return new NoOpCacheManager();
    }

    return new ConcurrentMapCacheManager();
}

如上所示,我們的邏輯檢查屬性是否設置為禁用緩存。如果是,我們可以返回一個 NoOpCacheManager 的實例,這是一個不執行緩存的緩存管理器。否則,我們可以返回我們的基於哈希的緩存管理器。

有了上述簡單的配置,我們可以在 Spring Boot 應用中禁用緩存。讓我們通過一個簡單的測試來驗證上述配置。

首先,我們需要修改我們在 application.properties 中定義的緩存屬性。對於我們的測試配置,我們可以使用 @TestPropertySource 覆蓋該屬性:

@SpringBootTest(classes = BookReviewApplication.class)
@ExtendWith(OutputCaptureExtension.class)
@TestPropertySource(properties = {
    "appconfig.cache.enabled=false"
})
public class BookReviewsLogicCacheDisabledUnitTest {
    // ...
}

現在,我們的測試將類似於之前的測試,即執行邏輯兩次。我們檢查 SQL 查詢日誌是否在當前測試中記錄了兩次,因為執行不會被緩存:

long count = Arrays.stream(logs)
   .filter(e -> e.contains(target))
   .count();

// count 2 means the select query log from 1st and 2nd execution.
assertEquals(2, count);

4. 結論

在本教程中,我們簡要介紹了 Spring Boot 中的緩存機制,並配置了應用程序中的緩存。我們還學習瞭如何在需要測試代碼某些部分時禁用緩存。此外,我們編寫了必要的測試,以驗證啓用和禁用緩存的功能。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.