Spring Cache API 的 Quarkus 扩展
虽然我们鼓励用户使用 Quarkus 注解进行缓存,但 Quarkus 仍然以 spring-cache
扩展的形式为 Spring Cache 注解提供了一个兼容层。
本指南解释了 Quarkus 应用程序如何利用众所周知的 Spring Cache 注解来为其 Spring bean 启用应用程序数据缓存。
先决条件
要完成本指南,您需要
-
大约 15 分钟
-
一个 IDE
-
已安装 JDK 17+ 并正确配置了
JAVA_HOME
-
Apache Maven 3.9.9
-
如果您想使用它,可以选择 Quarkus CLI
-
如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置
-
熟悉 Spring DI 扩展
创建 Maven 项目
首先,我们需要一个新项目。使用以下命令创建一个新项目
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用 Powershell,请将
-D
参数包裹在双引号中,例如"-DprojectArtifactId=spring-cache-quickstart"
此命令生成一个项目,该项目导入 spring-cache
和 spring-di
扩展。
如果已经配置了 Quarkus 项目,可以通过在项目根目录中运行以下命令将 spring-cache
扩展添加到项目中
quarkus extension add spring-cache
./mvnw quarkus:add-extension -Dextensions='spring-cache'
./gradlew addExtension --extensions='spring-cache'
这会将以下内容添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-cache</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-cache")
创建 REST API
让我们从创建一个服务开始,该服务将模拟对外部气象服务的极其缓慢的调用。创建 src/main/java/org/acme/spring/cache/WeatherForecastService.java
,内容如下
package org.acme.spring.cache;
import java.time.LocalDate;
import org.springframework.stereotype.Component;
@Component
public class WeatherForecastService {
public String getDailyForecast(LocalDate date, String city) {
try {
Thread.sleep(2000L); (1)
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return date.getDayOfWeek() + " will be " + getDailyResult(date.getDayOfMonth() % 4) + " in " + city;
}
private String getDailyResult(int dayOfMonthModuloFour) {
switch (dayOfMonthModuloFour) {
case 0:
return "sunny";
case 1:
return "cloudy";
case 2:
return "chilly";
case 3:
return "rainy";
default:
throw new IllegalArgumentException();
}
}
}
1 | 这是造成缓慢的原因。 |
我们还需要一个类,其中包含用户在询问未来三天的天气预报时发送的响应。以这种方式创建 src/main/java/org/acme/spring/cache/WeatherForecast.java
package org.acme.spring.cache;
import java.util.List;
public class WeatherForecast {
private List<String> dailyForecasts;
private long executionTimeInMs;
public WeatherForecast(List<String> dailyForecasts, long executionTimeInMs) {
this.dailyForecasts = dailyForecasts;
this.executionTimeInMs = executionTimeInMs;
}
public List<String> getDailyForecasts() {
return dailyForecasts;
}
public long getExecutionTimeInMs() {
return executionTimeInMs;
}
}
现在,我们只需要创建 src/main/java/org/acme/spring/cache/WeatherForecastResource.java
类来使用该服务和响应
package org.acme.spring.cache;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.jboss.resteasy.reactive.RestQuery;
@Path("/weather")
public class WeatherForecastResource {
@Inject
WeatherForecastService service;
@GET
public WeatherForecast getForecast(@RestQuery String city, @RestQuery long daysInFuture) { (1)
long executionStart = System.currentTimeMillis();
List<String> dailyForecasts = Arrays.asList(
service.getDailyForecast(LocalDate.now().plusDays(daysInFuture), city),
service.getDailyForecast(LocalDate.now().plusDays(daysInFuture + 1L), city),
service.getDailyForecast(LocalDate.now().plusDays(daysInFuture + 2L), city)
);
long executionEnd = System.currentTimeMillis();
return new WeatherForecast(dailyForecasts, executionEnd - executionStart);
}
}
1 | 如果省略 daysInFuture 查询参数,则未来三天的天气预报将从当天开始。否则,它将从当天加上 daysInFuture 值开始。 |
我们完成了!让我们检查一下一切是否正常工作。
首先,使用以下命令运行应用程序
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
然后,从浏览器调用 https://:8080/weather?city=Raleigh
。经过漫长的六秒钟后,应用程序将回答如下
{"dailyForecasts":["MONDAY will be cloudy in Raleigh","TUESDAY will be chilly in Raleigh","WEDNESDAY will be rainy in Raleigh"],"executionTimeInMs":6001}
响应内容可能因您运行代码的日期而异。 |
您可以尝试一次又一次地调用相同的 URL,它总是需要六秒钟才能回答。
启用缓存
现在您的 Quarkus 应用程序已启动并运行,让我们通过缓存外部气象服务响应来极大地提高其响应时间。按如下方式更新 WeatherForecastService
类
package org.acme.cache;
import java.time.LocalDate;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
public class WeatherForecastService {
@Cacheable("weather-cache") (1)
public String getDailyForecast(LocalDate date, String city) {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return date.getDayOfWeek() + " will be " + getDailyResult(date.getDayOfMonth() % 4) + " in " + city;
}
private String getDailyResult(int dayOfMonthModuloFour) {
switch (dayOfMonthModuloFour) {
case 0:
return "sunny";
case 1:
return "cloudy";
case 2:
return "chilly";
case 3:
return "rainy";
default:
throw new IllegalArgumentException();
}
}
}
1 | 我们只添加了这个注解(以及相关的导入语句)。 |
让我们尝试再次调用 https://:8080/weather?city=Raleigh
。您仍在等待很长时间才能收到答案。这是正常的,因为服务器刚刚重启并且缓存为空。
等一下!服务器在 WeatherForecastService
更新后自行重启?是的,这是 Quarkus 为开发人员提供的名为 live coding
的一项惊人功能。
现在缓存在之前的调用中已加载,尝试调用相同的 URL。这次,您应该得到一个超快的答案,其 executionTimeInMs
值接近 0。
让我们看看如果我们从未来的一天开始使用 https://:8080/weather?city=Raleigh&daysInFuture=1
URL 会发生什么。您应该在两秒钟后收到答案,因为请求的天数中已经有两个被加载到缓存中。
您还可以尝试使用不同的城市调用相同的 URL,并再次查看缓存的作用。第一次调用将花费六秒钟,随后的调用将立即回答。
恭喜!您只需一行代码就将应用程序数据缓存添加到了您的 Quarkus 应用程序中!
支持的功能
Quarkus 提供了与以下 Spring Cache 注解的兼容性
-
@Cacheable
-
@CachePut
-
@CacheEvict
请注意,在此 Spring Cache 注解扩展的第一个版本中,并非支持所有这些注解的功能(尝试使用不支持的功能时会记录适当的错误)。但是,计划在未来版本中添加其他功能。