Quarkus Extension for Spring Cache API
尽管鼓励用户使用Quarkus annotations for caching,Quarkus 仍然以 `spring-cache`扩展的形式提供 Spring Cache 注解兼容性层。 本指南解释了 Quarkus 应用程序如何利用众所周知的 Spring Cache 注解为其 Spring bean 启用应用程序数据缓存。
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
--no-code
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 --gradle
或 --gradle-kotlin-dsl
选项。
有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
-DprojectGroupId={create-app-group-id} \
-DprojectArtifactId={create-app-artifact-id} \
-DnoCode
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 -DbuildTool=gradle
或 -DbuildTool=gradle-kotlin-dsl
选项。
适用于 Windows 用户:
-
如果使用 cmd,(不要使用反斜杠
\
,并将所有内容放在同一行上) -
如果使用 Powershell,将
-D
参数用双引号引起来,例如"-DprojectArtifactId={create-app-artifact-id}"
此命令生成导入 `spring-cache`和 `spring-di`扩展的项目。
如果您已经配置了 Quarkus 项目,则可以通过在项目基本目录中运行以下命令将 `spring-cache`扩展添加到项目中:
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
这会将以下内容添加到构建文件中:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-cache</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-cache")
Creating the 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
然后,在浏览器中调用 http://localhost: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,它总是需要六秒钟来响应。
Enabling the cache
现在,您的 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 | 我们只添加了此注释(当然还有关联的导入)。 |
让我们再次尝试调用 http://localhost:8080/weather?city=Raleigh
。收到答复之前仍需要等待很长的时间。这是因为服务器刚刚重启并清空了缓存。
等等!服务器在 WeatherForecastService`更新后自行重启?是的,这是 Quarkus 开发人员非常棒的功能之一,称为 `live coding
。
由于上一次调用中加载了缓存,请尝试再次调用相同的 URL。这次,您应该得到超快速的答复,其中 `executionTimeInMs`值接近 0。
我们使用 `http://localhost:8080/weather?city=Raleigh&daysInFuture=1`URL 从未来的一天开始尝试下会发生什么。您应该在两秒后收到答复,因为请求的两天数据已加载到缓存中。
您还可以尝试用一个不同的城市来调用相同的 URL 并再次看到缓存如何运行。第一次调用将持续 6 秒钟,而接下来的调用会立即得到答复。
恭喜!您刚刚通过一行代码向 Quarkus 应用程序添加了应用程序数据缓存!
Supported features
Quarkus 提供了对以下 Spring Cache 注释的兼容性:
-
@Cacheable
-
@CachePut
-
@CacheEvict
请注意,在此第一个版本的 Spring Cache 注释扩展中,并非所有这些注释的功能都得到支持(在尝试使用不支持的功能时会记录适当的错误)。不过,未来版本中将会计划新增其他功能。
More Spring guides
Quarkus 还有更多 Spring 兼容性功能。请参阅以下指南以获取更多详情: