Extensão do Quarkus para a API do Spring Cache
Embora os usuários sejam incentivados a usar as anotações do Quarkus para armazenamento em cache , o Quarkus fornece uma camada de compatibilidade para as anotações do Spring Cache na forma da extensão spring-cache
.
Este guia explica como um aplicativo Quarkus pode aproveitar as conhecidas anotações do Spring Cache para habiblitar o cache de dados do aplicativo para seus beans Spring.
Pré-requisitos
Para concluir este guia, você precisa:
-
Cerca de 15 minutos
-
Um IDE
-
JDK 17+ installed with
JAVA_HOME
configured appropriately -
Apache Maven 3.9.9
-
Opcionalmente, o Quarkus CLI se você quiser usá-lo
-
Opcionalmente, Mandrel ou GraalVM instalado e configurado apropriadamente se você quiser criar um executável nativo (ou Docker se você usar uma compilação de contêiner nativo)
-
Alguma familiaridade com a extensão Spring DI
Criar o projeto Maven
Primeiro, precisamos de um novo projeto. Crie um novo projeto com o seguinte comando:
Para usuários do Windows:
-
Se estiver usando cmd, (não use barra invertida '\' e coloque tudo na mesma linha)
-
Se estiver usando o Powershell, envolva os parâmetros '-D' entre aspas duplas, por exemplo, '"-DprojectArtifactId=spring-cache-quickstart"'
Esse comando gera um projeto que importa as extensões spring-cache
e spring-di
.
Se você já tem seu projeto Quarkus configurado, você pode adicionar a extensão spring-cache
para seu projeto executando o seguinte comando no diretório base do projeto:
quarkus extension add spring-cache
./mvnw quarkus:add-extension -Dextensions='spring-cache'
./gradlew addExtension --extensions='spring-cache'
Isto irá adicionar o seguinte trecho no seu arquivo de build:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-cache</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-cache")
Criando a API REST
Vamos começar criando um serviço que simulará uma chamada extremamente lenta para um serviço meteorológico externo. Crie src/main/java/org/acme/spring/cache/WeatherForecastService.java
com o seguinte conteúdo:
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 | É daí que vem a lentidão. |
Também precisamos de uma classe que contenha a resposta enviada aos usuários quando eles solicitarem a previsão do tempo para os próximos três dias. Crie src/main/java/org/acme/spring/cache/WeatherForecast.java
dessa forma:
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;
}
}
Agora, precisamos criar a classe src/main/java/org/acme/spring/cache/WeatherForecastResource.java
para usar o serviço e responder:
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 | Se o parâmetro de consulta daysInFuture for omitido, a previsão do tempo para três dias começará a partir do dia atual. Caso contrário, ela começará a partir do dia atual mais o valor daysInFuture . |
Já está tudo pronto! Vamos verificar se tudo está funcionando.
Primeiro, execute o aplicativo usando:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
Em seguida, chame o http://localhost:8080/weather?city=Raleigh
de um navegador. Após seis longos segundos, o aplicativo responderá alguma coisa parecido com isto:
{"dailyForecasts":["MONDAY will be cloudy in Raleigh","TUESDAY will be chilly in Raleigh","WEDNESDAY will be rainy in Raleigh"],"executionTimeInMs":6001}
O conteúdo da resposta pode variar dependendo do dia em que você executar o código. |
Você pode tentar ligar para a mesma URL várias vezes, mas ele sempre levará seis segundos para responder.
Ativação do cache
Agora que seu aplicativo Quarkus está em funcionamento, vamos melhorar muito seu tempo de resposta armazenando em cache as respostas do serviço meteorológico externo. Atualize a classe WeatherForecastService
da seguinte forma:
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 | Adicionamos apenas essa anotação (e a importação associada, é claro). |
Vamos tentar chamar http://localhost:8080/weather?city=Raleigh
novamente. Você ainda está esperando muito tempo antes de receber uma resposta. Isso é normal, pois o servidor acabou de ser reiniciado e o cache estava vazio.
Espere um segundo! O servidor reiniciou após a atualização do WeatherForecastService
? Sim, esse é um dos incríveis recursos do Quarkus para desenvolvedores, chamado live coding
.
Agora que o cache foi carregado durante a chamada anterior, tente chamar a mesma URL. Dessa vez, você deve obter uma resposta super rápida com um valor executionTimeInMs
próximo de 0.
Vamos ver o que acontece se começarmos a partir de um dia no futuro usando o http://localhost:8080/weather?city=Raleigh&daysInFuture=1
URL. Você deve obter uma resposta dois segundos depois, pois dois dos dias solicitados já foram carregados no cache.
Você também pode tentar chamar a mesma URL com uma cidade diferente e ver o cache em ação novamente. A primeira chamada levará seis segundos e as seguintes serão respondidas imediatamente.
Parabéns! Você acabou de adicionar o cache de dados do aplicativo a sua aplicação Quarkus com uma única linha de código!
Recursos suportados
O Quarkus oferece compatibilidade com as seguintes anotações do Spring Cache:
-
@Cacheable
-
@CachePut
-
@CacheEvict
Observe que, nesta primeira versão da extensão de anotações do Spring Cache, nem todos os recursos dessas anotações são suportados (com os erros apropriados sendo registrados ao tentar usar um recurso não suportado). No entanto, recursos adicionais estão planejados para versões futuras.
Mais guias de Spring
O Quarkus possui mais funcionalidades compatíveis com o Spring. Para mais informações, consulte os seguintes guias: