spring 如何使用Java Sping Boot Cache

20jt8wwn  于 2023-04-04  发布在  Spring
关注(0)|答案(4)|浏览(146)

我有Java Sping Boot 应用程序。我想使用缓存来存储经常读取的数据。为此,我在我的jar中包含了以下依赖项

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

我还使用了@EnableCaching注解

@EnableCaching 
public class SpringBootConfig {
public static void main(String[] args) throws Exception {
    SpringApplication.run(SpringBootConfig.class, args);    
  }
}

使用了@Cacheable annotation和返回要缓存的数据的函数

@Cacheable(value = "country",key = "'countryCache'+#countryCode")
private Country getCountry(String countryCode) {
    return new Country(countryCode);
}

但是我仍然无法缓存数据。是否有我遗漏的内容?

piok6c0g

piok6c0g1#

注解@Cacheable仅适用于允许被拦截的公开方法。但如果需要,您可以获得“CacheManager”服务并在代码中使用它来内部处理私有方法中该高速缓存。但只有解决一些“特殊”问题,通常的方法是注解公共方法。
另外,如果您只使用starter,那么您只使用了Spring的基本和糟糕的实现,一个简单的内存缓存。
考虑一下你的应用程序将如何工作(单个应用程序,分布式应用程序,缓存的短/长数据量......)以及内存消耗,以添加对任何支持的缓存管理器(如ehCache,Hazelcast,Caffeine......)的依赖,以满足你的需求并提高你的缓存性能。

f5emj3cl

f5emj3cl2#

你已经看过Getting Started Guide for Caching Data了吗?
有一段将解释为什么该高速缓存在您的代码中不起作用。
@EnableCaching annotation触发一个后处理器,该后处理器检查每个Spring bean是否在public方法上存在缓存annotation。如果找到这样的annotation,则会自动创建一个代理来拦截方法调用并相应地处理缓存行为。
因为你的getCountry方法是private,所以缓存不会起作用。也许你缓存调用方法的结果是合理的?

goqiplq2

goqiplq23#

只有通过代理进入的外部方法调用才会被拦截。这意味着自调用,实际上,目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法标记为@Cacheable,也不会在运行时导致实际的缓存拦截。
另外,我建议在spring Boot 中使用Ehcache实现,它允许您进行条件缓存。

hm2xizp9

hm2xizp94#

这是我在我的小应用程序中所做的,它是一个大应用程序的一部分:

  • 这里我使用CaffeineCache
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>
  • 为了避免与-可能是大型应用程序中的另一个名称-冲突,为了区分,我使用了限定符:
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import java.time.Duration;

@Configuration
public class CacheManagement extends CachingConfigurerSupport {

    @Bean("my-cache-manager") // <-- here is my qualifier
    @Override
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(List.of(
                new CaffeineCache("myFirstCache", Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(myFirstCacheDuration)).build()),
                new CaffeineCache("mySecondCache", Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(mySecondCacheDuration)).build())
                //, ... if you have more named caches
        ));
        return cacheManager;
    }

}
  • 我的客户端中需要缓存的响应:
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.http.RequestEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.cache.annotation.Cacheable;

@Service
public class MyFirstClient {

    @CircuitBreaker(name = "myFirst")
    @Retryable(value = {RetryableException.class}, backoff = @Backoff(delayExpression = "#{${retryable.backoff.delayexpression}}"))
    @Cacheable(
            key = "#root.methodName",  // <- or key = "#idOfYourModel" if your model has one
            cacheNames = "myFirstCache",
            cacheManager = "my-cache-manager"
    )
    public MyFirstResponse getData(Map<String, String> headers) {
         return myRestClient.execute(
                RequestEntity
                        .get(UriComponentsBuilder.fromHttpUrl(apiEndPoint).queryParam("myQueryParam", myQueryParam).build().toUri())
                        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .header("traceId", headers.get("traceId"))
                        .build()
        ).getBody();
    }
}
  • 在我的例子中,当我在@Cacheable中排除key时,它使用traceId作为键,这导致每个不同的traceId都有一组新的数据。危险的是,同一组数据占用越来越多的内存!

相关问题