2025/6/9

Spring Boot 3 Cache

spring 提供對 cache 的 API 介面

  • org.springframework.cache.Cache

  • org.springframework.cache.CacheManager

如果 application 沒有註冊 CacheManager 或 CacheResolver,spring 會依照以下順序檢測 cache component

Generic -> JCache (JSR-107) (EhCache3, Hazelcast, Infinispan..) -> Hazelcast -> Infinispan -> Couchbase -> Redis -> Caffeine -> Cache2k -> Simple

org.springframework.boot.autoconfigure.AutoConfiguration.imports 有註冊自動設定類別

自動設定類別為 CacheAutoConfiguration,參數綁定類別為 CacheProperties

設定參數 spring.cache.*

pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

                <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

application.yml

spring:
  cache:
    type: none
    # none 代表禁用 cache
    # type: redis

type 可設定為 COUCHBASE/GENERIC/REDIS/HAZELCAST/CACHE2K/CAFFEINE/JCACHE/INFINISPAN/NONE/SIMPLE


預設簡單 cache

如果沒有設定任何 cache 元件,就是使用 Simple,也就是 thread-safe 的 ConcurrentHashMap

pom.xml 加上 web

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

application 要加上 @EnableCaching

@EnableCaching
@SpringBootApplication
public class CacheApplication {

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

}

先建立一個兩數相乘的 cache service

CacheService.java

package com.test.cache;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class CacheService {

    @Cacheable("calc")
    public int multiply(int a, int b) {
        int c = a * b;
        log.info("{} * {} = {}", a, b, c);
        return c;
    }

}

@Cacheable 代表該 method 使用 cache,這是用 AOP 的方法做的

一個測試用的 web service

package com.test.cache;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
public class CacheController {

    private final CacheService cacheService;

    @RequestMapping("/multiply")
    public int multiply(@RequestParam("a") int a,
                        @RequestParam("b") int b) {
        return cacheService.multiply(a, b);
    }

}

測試網址 http://localhost:8080/multiply?a=2&b=3

重複多測試幾次,console log 上面都還是只有一行 log。代表重複的參數,會直接從 cache 取得結果,不會進入 service

com.test.cache.CacheService              : 2 * 3 = 6

Redis Cache

pom.xml 加上 redis

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

application.yml

spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password: password

redis-cli

# redis-cli -a password
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> keys *
1) "calc::SimpleKey [2, 3]"
127.0.0.1:6379> get "calc::SimpleKey [2, 3]"
"\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x06"

可在建立 cache 名稱,time-to-live 代表只存放 10s

spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password: password
  cache:
    type: redis
    cache-names: "calc,test"
    redis:
      time-to-live: "10s"

針對不同 cache 設定不同規則,可透過 RedisCacheManagerBuilderCustomizer

package com.test.cache;

import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;

import java.time.Duration;

@Configuration
public class CacheConfiguration {

    /**
     * 比 application.yml 的設定內容優先權高
     * @return
     */
    @Bean
    public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
        return (builder) -> builder
                .withCacheConfiguration("calc", RedisCacheConfiguration
                        .defaultCacheConfig().entryTtl(Duration.ofSeconds(5)))
                .withCacheConfiguration("test", RedisCacheConfiguration
                        .defaultCacheConfig().entryTtl(Duration.ofMinutes(10)));

    }

}

網址 http://localhost:8080/multiply?a=2&b=3

calc, test 兩個 redis cache 都有資料

# redis-cli -a password -n test
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> keys *
1) "calc::SimpleKey [2, 3]"

# redis-cli -a password -n calc
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> keys *
1) "calc::SimpleKey [2, 3]"

沒有留言:

張貼留言