Eclipse MicroProfile and Spring Boot are often thought of as separate and distinct APIs when developing Java microservices. Developers default to their mental muscle memory by leveraging the APIs that they use on a daily basis. Learning new frameworks and runtimes can be a significant time investment. This article aims to ease the introduction to some popular MicroProfile APIs for Spring developers by enabling them to utilize the Spring APIs they already know while benefiting from significant new capabilities offered by Quarkus.
More specifically, this article covers the scope and details of the Spring APIs supported by Quarkus so Spring developers have a grasp of the foundation they can build on with MicroProfile APIs. The article then covers MicroProfile APIs that Spring developers will find helpful in the development of microservices. Only a subset of MicroProfile is covered.
Why Quarkus? Live coding is one reason, where any change is automatically reloaded whether MicroProfile, Spring, or any other Java API. Just run mvn quarkus:dev
. That's it. A second compelling reason is that the example project's Person service, which compiles Spring, MicroProfile, and JPA APIs to a native binary using GraalVM's native-image, starts in 0.055 seconds and uses ~90MB of RAM (RSS) after hitting the application RESTful endpoints. Run mvn package -Pnative
to compile to a native binary. That's it.
This article will not go into detailed comparisons, but should give the Spring developers an understanding of how the Spring and MicroProfile APIs can be used together with Quarkus.
Containers and Kubernetes
This article will only cover Kubernetes support at a high level to keep this article short(er), but it is important to briefly discuss. One of the Quarkus key value propositions is "Kubernetes-native Java," where the goal is to minimize the memory footprint and reduce the startup time. The reduced memory footprint helps drive up the density of applications on the same hardware, reducing overall costs.
Quarkus also supports auto-generation of a Kubernetes resources, and guides are available on deploying to Kubernetes and Red Hat OpenShift as well. Additionally, a Dockerfile.jvm (JVM packaging) and Dockerfile.native (native binary packaging) are generated automatically for container creation.
Last, given that Quarkus considers Kubernetes a target deployment environment, it forgoes using Java frameworks when inherent Kubernetes capabilities are available. Table 1 briefly maps Java frameworks typically used by Spring developers with Kubernetes built-in capabilities.
Table 1: Java framework to Kubernetes mapping
Service, Replication Controller("server side")
Capability | Traditional Spring Boot | Kubernetes |
Service discovery | Eureka | DNS |
Configuration | Spring Cloud Config | Config Maps / Secrets |
Load balancing | Ribbon ("client side") | Service, Replication Controller("server side") |
Compiling and running the example code
This article is accompanied by an example project that utilizes Spring and MicroProfile APIs together in the same project, and even the same Java class. The code can be compiled and run from the command line. Be sure to read the README.md for instructions.
Spring Framework APIs
Dependency Injection
Quarkus supports many Contexts and Dependency Injection (CDI) APIs and Spring Dependency Injection (Spring DI) APIs. MicroProfile, Java EE, and Jakarta EE developers will be very familiar with CDI. Spring developers, on the other hand, can use the Quarkus Extension for Spring DI API for Spring DI compatibility. Table 2 covers a sample of the supported Spring DI APIs.
The example project utilizes both CDI and Spring Dependency Injection, and the Quarkus Spring DI Guide goes into greater detail along with additional examples.
Table 2: Sample of supported Spring DI APIs
Spring DI |
Examples |
Constructor Injection |
public PersonSpringController( PersonSpringRepository personRepository, // injected PersonSpringMPService personService) { // injected this.personRepository = personRepository; this.personService = personService; } |
Field Injection @Autowired @Value |
@Autowired @RestClient SalutationRestClient salutationRestClient; @Value("${fallbackSalutation}") String fallbackSalutation; |
@Bean @Configuration |
@Configuration public class AppConfiguration { @Bean(name = "capitalizeFunction") public StringFunction capitalizer() { return String::toUpperCase; } } |
@Component |
@Component("noopFunction") public class NoOpSingleStringFunction implements StringFunction { @Override public String apply(String s) { return s; } } |
@Service |
@Service public class MessageProducer { @Value("${greeting.message}") String message; public String getPrefix() { return message; } } |
Web framework
MicroProfile developers will be comfortable with the Quarkus support for JAX-RS, MicroProfile Rest Client, JSON-P and JSON-B as the core web programming model. Spring developers may be surprised to know that Quarkus has recently added Spring Web API support, specifically around Spring REST-related APIs. As with Spring DI, the goal of Spring Web API support is to make Spring developers feel at home using Spring Web API and MicroProfile APIs together. Table 3 covers a sample of supported Spring Web APIs.
The example project utilizes Spring Web and MicroProfile Rest Client APIs, and the Quarkus Spring Web Guide goes into greater detail along with additional examples.
Table 3: Sample of supported Spring Web APIs
Spring Web |
Examples |
@RestController @RequestMapping |
@RestController @RequestMapping("/person") public class PersonSpringController { ... ... ... } |
@GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping @RequestParam @RequestHeader @MatrixVariable @PathVariable @CookieValue @RequestBody @ResponseStatus @ExceptionHandler @RestControllerAdvice (partial) |
@GetMapping(path = "/greet/{id}", produces = "text/plain") public String greetPerson( @PathVariable(name = "id") long id) { ... ... ... } |
Spring Data JPA
MicroProfile developers will be comfortable with the Quarkus JPA support using Hibernate ORM. Spring developers, have no fear! Quarkus has support for commonly used Spring Data JPA annotations and types. Table 4 covers a sample of supported Spring Data JPA APIs.
The example project utilizes the Spring Data JPA repository APIs, and the Quarkus Spring Data JPA Guide goes into greater detail along with additional examples.
Table 4: Sample of supported Spring Data JPA APIs
Spring Data JPA |
Examples |
CrudRepository |
public interface PersonRepository extends JpaRepository, PersonFragment { ... } |
Repository
JpaRepository PagingAndSortingRepository |
public class PersonRepository extends Repository { Person save(Person entity); Optional findById(Person entity); } |
Repository Fragments |
public interface PersonRepository extends JpaRepository, PersonFragment { ... } |
Derived query methods
|
public interface PersonRepository extends CrudRepository { List findByName(String name); Person findByNameBySsn(String ssn); Optional findByNameBySsnIgnoreCase(String ssn); Boolean existsBookByYearOfBirthBetween( Integer start, Integer end); } |
User-defined queries |
public interface MovieRepository extends CrudRepository { Movie findFirstByOrderByDurationDesc(); @Query("select m from Movie m where m.rating = ?1") Iterator findByRating(String rating); @Query("from Movie where title = ?1") Movie findByTitle(String title); } |
MicroProfile APIs
Fault tolerance
Fault tolerance patterns are critical to prevent cascading failures and to create a reliable microservice architecture. Hystrix circuit-breaking has been a "go-to" fault tolerance pattern for Spring developers for years. However, Hystrix is in maintenance mode. MicroProfile Fault Tolerance is in active development and developers have been using it in production for years now. Quarkus recommends using MicroProfile Fault Tolerance APIs to improve service reliability. Table 5 covers a sample of the MicroProfile Fault Tolerance APIs.
The example project uses the MicroProfile Fault Tolerance API, @Timeout and @Fallback in particular. The Quarkus Fault Tolerance Guide goes into greater detail along with additional examples.
Table 5: Sample of MicroProfile Fault Tolerance APIs
MicroProfile Fault Tolerance |
Description | Examples |
@Asynchronous | Execute logic on a separate thread |
@Asynchronous @Retry public Future<String> getSalutation() { ... return future; } |
@Bulkhead | Limits number of concurrent requests |
@Bulkhead(5) public void fiveConcurrent() { makeRemoteCall(); //... } |
@CircuitBreaker | Gracefully handle faults and fault recovery |
@CircuitBreaker(delay=500 // milliseconds failureRatio = .75, requestVolumeThreshold = 20, successThreshold = 5) @Fallback(fallbackMethod = "fallback") public String getSalutation() { makeRemoteCall(); //... } |
@Fallback | Alternative logic called upon failure |
@Timeout(500) // milliseconds @Fallback(fallbackMethod = "fallback") public String getSalutation() { makeRemoteCall(); //... } public String fallback() { return "hello"; } |
@Retry | Retry a request |
@Retry(maxRetries=3) public String getSalutation() { makeRemoteCall(); //... } |
@Timeout | Wait period before assuming failure |
@Timeout(value = 500 ) // milliseconds @Fallback(fallbackMethod = "fallback") public String getSalutation() { makeRemoteCall(); //... } |
Service Health
Platforms like Kubernetes utilize probes to check the health of a container. Spring developers utilize a custom HealthIndicator and Spring Boot Actuator to expose the health of a service to the underlying platform. With Quarkus, Spring developers can utilize MicroProfile Health to expose the health of a service. A default liveness check is provided, and developers can provide custom liveness and readiness checks as well. Table 6 covers a sample of the MicroProfile Health APIs.
The example project uses MicroProfile Health to expose the readiness of an application. The Quarkus Health Guide goes into greater detail along with additional examples.
Table 6: Sample of MicroProfile Health APIs
MicroProfile Health |
Description | Examples |
@Liveness | Platform will reboot unhealthy containerized applications.
Endpoint: |
@Liveness public class MyHC implements HealthCheck { public HealthCheckResponse call() { ... return HealthCheckResponse .named("myHCProbe") .status(ready ? true:false) .withData("mydata", data) .build(); } |
@Readiness | Platform will not direct traffic to containerized applications that are not ready.
Endpoint: |
@Readiness public class MyHC implements HealthCheck { public HealthCheckResponse call() { ... return HealthCheckResponse .named("myHCProbe") .status(live ? true:false) .withData("mydata", data) .build(); } |
Metrics
Applications expose metrics for operational reasons (like performance SLAs) and non-operational reasons (like business SLAs). Spring developers typically utilize Spring Boot Actuator and Micrometer to expose metrics. Quarkus utilizes MicroProfile Metrics to expose base (JVM & Operating System), Vendor (Quarkus), and application metrics. MicroProfile Metrics requires JSON and OpenMetrics (Prometheus) output formats be supported by implementations. Table 7 covers a sample of the MicroProfile Metrics APIs.
The example project uses MicroProfile Metrics to expose the application metrics. The Quarkus Metrics Guide goes into greater detail along with additional examples.
Table 7: Sample of MicroProfile Metrics APIs
MicroProfile Metrics |
Description | Examples |
@Counted | Denotes a counter which counts the invocations of the annotated object. |
@Counted(name = "fallbackCounter", displayName = "Fallback Counter", description = "Fallback Counter") public String salutationFallback() { return fallbackSalutation; } |
@ConcurrentGauge | Denotes a gauge which counts the parallel invocations of the annotated object. |
@ConcurrentGuage( name = "fallbackConcurrentGauge", displayName="Fallback Concurrent", description="Fallback Concurrent") public String salutationFallback() { return fallbackSalutation; } |
@Gauge | Denotes a gauge, which samples the value of the annotated object. |
@Metered(name = "FallbackGauge", displayName="Fallback Gauge", description="Fallback frequency") public String salutationFallback() { return fallbackSalutation; } |
@Metered | Denotes a meter which tracks the frequency of invocations of the annotated object. |
@Metered(name = "MeteredFallback", displayName="Metered Fallback", description="Fallback frequency") public String salutationFallback() { return fallbackSalutation; } |
@Metric | An annotation that contains the metadata information when requesting a metric to be injected or produced. |
@Metric @Metered(name = "MeteredFallback", displayName="Metered Fallback", description="Fallback frequency") public String salutationFallback() { return fallbackSalutation; } |
@Timed | Denotes a timer which tracks duration of the annotated object. |
@Timed(name = "TimedFallback", displayName="Timed Fallback", description="Fallback delay") public String salutationFallback() { return fallbackSalutation; } |
Metrics Endpoints | ||
Application metrics | http://localhost:8080/metrics/application | |
Base metrics | http://localhost:8080/metrics/base | |
Vendor metrics | http://localhost:8080/metrics/vendor | |
All metrics | http://localhost:8080/metrics |
MicroProfile Rest Client
Microservices often expose RESTful endpoints, requiring a client API to consume a RESTful endpoint. Spring developers typically use a RestTemplate to consume RESTful endpoints. Quarkus supports the MicroProfile Rest Client API to do the same. Table 8 covers a sample of MicroProfile Rest Client APIs.
The example project uses MicroProfile Rest Client to consume RESTful endpoints. The Quarkus Rest Client Guide goes into greater detail along with additional examples.
Table 8: Sample of MicroProfile Rest Client APIs
MicroProfile Rest Client Feature |
Description | Examples |
@RegisterRestClient | Register a typed Java interface as a REST client |
@RegisterRestClient @Path("/") public interface MyRestClient { @GET @Produces(MediaType.TEXT_PLAIN) public String getSalutation(); } |
@RestClient | Decorate instance injection of a typed REST client interface |
@Autowired // or @Inject @RestClient MyRestClient restClient; |
Invocation | Invoke REST endpoint |
System.out.println( restClient.getSalutation()); |
mp-rest/url | Specify rest endpoint |
application.properties: org.example.MyRestClient/mp-rest/url= http://localhost:8081/myendpoint |
Summary
This article provided an overview, primarily for Spring developers, on using Spring APIs together and MicroProfile APIs with Quarkus. Spring developers can now use some of the APIs they know and love, combined with MicroProfile APIs, to live code Java microservices and then compile them to a native binary savings 100's of MB of RAM while starting in milliseconds.
Note: The Quarkus guides provide more details around Spring and MicroProfile API support, and much more.
Last updated: July 1, 2020