Spring(Spring Boot)のCache機能を使ってメソッドの呼び出し結果をキャッシュしておくサンプルです。
動作確認バージョン
- Spring Boot 2.7.5
- Spring Framework 5.3.23
Cache機能の有効化
@EnableCaching
を付与するとSpring Bootさんの自動コンフィギュレーション機能によってSpringのCache機能が利用できるようになります。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching // ★★★アノテーションを追加
public class SpringCacheDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCacheDemoApplication.class, args);
}
}
キャッシュ対象のメソッドを指定
結果をキャッシュしたいメソッドに@Cacheable
を付与する。下記のサンプルではキャッシュ名を「rates
」にしています。
package com.example.demo;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.util.UUID;
@Component
public class RateRepository {
@Cacheable("rates") // ★★★ キャッシュしたいメソッドにアノテーションを追加
public Rate getRate(String unitId, String groupId, String type, String baseDate) {
Rate rate = new Rate();
rate.setId(UUID.randomUUID().toString());
rate.setRate(BigDecimal.valueOf(new SecureRandom().nextLong()));
return rate;
}
}
動かしてみる
以下のJUnitを動かすと、同じパラメータでメソッドを呼び出した場合は、キャッシュ済みのインスタンスが返却されることがわかります。
@SpringBootTest
class SpringCacheDemoApplicationTests {
@Autowired
RateRepository rateRepository;
@Test
void contextLoads() {
Rate rate1 = rateRepository.getRate("U001", "G001", "Credit", "20221114");
Rate rate2 = rateRepository.getRate("U001", "G001", "Credit", "20221114");
// ★★★ 同じインスタンスが返却される
Assertions.assertThat(rate2.getId()).isEqualTo(rate1.getId());
Assertions.assertThat(rate2.getRate()).isEqualTo(rate1.getRate());
Assertions.assertThat(rate2).isSameAs(rate1);
}
}
テストクラスでキャッシュを削除する
テストの場合は、他のテストに影響を与えないためにキャッシュ済みのデータをテスト毎に破棄しておいた方が良いので・・・テストクラスにCacheManager
をインジェクションしてキャッシュをクリアしておきます。
@SpringBootTest
class SpringCacheDemoApplicationTests {
@Autowired
RateRepository rateRepository;
@Autowired
CacheManager cacheManager;
// ★★★ テスト実施前後にキャッシュをクリアする
@BeforeEach
@AfterEach
void clearCache() {
cacheManager.getCacheNames().stream()
.map(cacheManager::getCache)
.filter(Objects::nonNull)
.forEach(Cache::clear);
}
@Test
void contextLoads() {
Rate rate1 = rateRepository.getRate("U001", "G001", "Credit", "20221114");
Rate rate2 = rateRepository.getRate("U001", "G001", "Credit", "20221114");
Assertions.assertThat(rate2.getId()).isEqualTo(rate1.getId());
Assertions.assertThat(rate2.getRate()).isEqualTo(rate1.getRate());
Assertions.assertThat(rate2).isSameAs(rate1);
// ★★★ 明示的にキャッシュを削除してみる
clearCache();
// ★★★ キャッシュ削除後に同じパラメータでメソッドを呼び出しみる
Rate rate3 = rateRepository.getRate("U001", "G001", "Credit", "20221114");
// ★★★ Cacheクリア後は別のインスタンスが返却される
Assertions.assertThat(rate3.getId()).isNotEqualTo(rate1.getId());
Assertions.assertThat(rate3.getRate()).isNotEqualTo(rate1.getRate());
Assertions.assertThat(rate3).isNotSameAs(rate1);
}
}