While Quarkus is more in line with Java EE standards such as CDI and JAX-RS, Spring provides an alternative modular architecture that revolves around its core container.
|Build time class initialization||yes||no|
|Kubernetes resources generation||yes||no|
|GraalVM native images support||yes||limited|
|Dependency injection & components management||CDI, Spring DI extension||Spring Core|
|Web / REST API development||JAX-RS, Spring Web extension||Spring MVC, Spring WebFlux, JAX-RS|
|Reactive / non-blocking web stack||Vert.x||Reactor Netty|
|REST Clients||Vert.x WebClient, MicroProfile Rest Client||RestTemplate, WebClient, Feign|
|JSON serialization||JSON-B, Jackson||Jackson, JSON-B|
|Simplified data access||Panache, Spring Data JPA extension||Spring Data: JPA, JDBC, MongoDB, LDAP, KeyValue|
|Reactive data access||Reactive SQL Clients, Reactive MongoDB Client||Spring Data R2DBC|
|Authentication & authorization||Elytron Security||Spring Security|
|Application monitoring||MicroProfile Health, MicroProfile Metrics||Spring Boot Actuator|
|Resilience & Fault tolerance||MicroProfile Fault Tolerance||Netflix Hystrix|
|Online project starter||https://code.quarkus.io/||https://start.spring.io/|
Performance overview of Quarkus vs Spring Boot
The illustrations of this section show the startup time and heap usage after GC of a Quarkus app with RESTEasy and a Spring Boot Web app. Both apps expose one simple endpoint. JDK 1.8.0_201 was used to perform the tests.
Startup time & memory footprint
In this example, Quarkus is 2 times faster to start and consumes half the heap size used by Spring.
Because part of the initialization is done at build time, Quarkus has a reduced startup time.
The small memory footprint of Quarkus results from the fact that the application will only contain the classes that are actually needed at runtime.
Time to first request
To measure more accurately how long a framework needs to start, I wrote a simple node.js script to calculate the first response time to API requests after starting up Quarkus and Spring Boot. I shared it the following github repository.
I launched the script 5 times with a Quarkus runnable jar and the same times with a Spring Boot uber jar (the same jars used in the previous section).
The results were in accordance with the previously reported startup times.
On average, Quarkus took 1862 milliseconds to respond to the first request, while Spring Boot took 3580 milliseconds.
You can checkout the following article for a more in depth performance comparison in a real use case.
GraalVM native performance
Quarkus is an order of magnitude faster to start when packaged as a GraalVM native image and uses a much smaller heap.
This article can give you an idea of the startup time and memory footprint of frameworks that support native images such as Quarkus, Micronaut and Helidon.
GraalVM native images support
The Spring Framework only provides experimental support for GraalVM native images as of 5.1. A more mature solution will be provided starting from the 5.3 version (Q2 2020).
In the other hand, Quarkus already provides out of the box support for GraalVM native images, and makes it easy to circumvent the limitations of Java native images by applying the following adaptations:
By default, the GraalVM removes all the classes/methods/fields that are not used directly in a call tree.
For this reason, it is necessary to manually register the classes that will be manipulated using reflection (such as model/domain classes that require serialization using frameworks that use reflection) either by annotating them with
@RegisterForReflection or using a configuration file when the class cannot be modified (third-party library for example).
Some classes will not be required to be registered for reflection when Quarkus can automatically deduce them at build time by analyzing the code (REST methods for example).
All classes are initialized by default at build time by Quarkus. When a class initialization must be done at runtime, the
--initialize-at-run-time=<package or class> build argument can be used.
Proxy classes must be defined at image build time by specifying the list of interfaces that they implement using the build argument
Only the web resources found in
META-INF/resources will be included by default in the native executable. To bundle more resources into the native executable, a
resources-config.json configuration file can be used.