Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Spring Boot on Quarkus: Magic or madness?

February 9, 2021
Eric Deandrea
Related topics:
JavaMicroservicesQuarkusSpring Boot
Related products:
Red Hat OpenShift

Share:

    Want to learn more about developing applications with Quarkus? Download our free ebook Quarkus for Spring Developers, which helps Java developers familiar with Spring make a quick and easy transition.

    Quarkus is a Java stack tailored for OpenJDK HotSpot (or OpenJ9 on zSeries) and GraalVM, crafted from optimized Java libraries and standards. It is a good choice for building highly-scalable applications while using lower amounts of CPU and memory resources than other Java frameworks. These applications can be traditional web applications, serverless applications, or even functions as a service.

    There are many documented instances of organizations migrating their applications to Quarkus. In this article, let's see one such migration path from Spring Boot to Quarkus that is part magic and part madness! The magic will be some hand waving and performing the migration without changing a single line of code. The madness will be trying to figure out how it was done.

    The Application

    The application is a simple “to-do” task management system. The user can enter to-do items and then check them off once done. These items are stored in a PostgreSQL database. All the application’s source code can be found here. There’s a version that uses Gradle instead of Maven as a build tool on the gradle branch.

    Start the database

    The application requires a PostgreSQL database, so the first thing we will do is use Docker or Podman to start an instance locally:

    docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name tododb -e POSTGRES_USER=todo -e POSTGRES_PASSWORD=todo -e POSTGRES_DB=tododb -p 5432:5432 postgres:13
    

    A PostgreSQL 11.5 instance on port 5432 should now be running. The tododb schema accessible by the user todo with the password todo should be created.

    Run the application

    Run the application by issuing the command ./mvnw clean spring-boot:run. If you want to use Gradle instead of Maven, first switch to the gradle branch (git checkout gradle) and run the command ./gradlew clean bootRun.

    You should see the standard Spring Boot banner:

      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.5.2)
    
    INFO 70823 --- [  restartedMain] i.q.todospringquarkus.TodoApplication    : Started TodoApplication in 4.392 seconds (JVM running for 5.116)
    

    Take note of the startup time. We'll revisit this in a bit.

    Once the application is running, navigate to http://localhost:8080 in your favorite browser.  You should see the main application screen as shown here in Figure 1.

    MainApplicationScreen
    Figure 1 - Main Application Screen
    Figure 1: Initial application screen.

    Play around with the application a bit. Type a new todo into the text box and press Enter. That todo will show up in the list, as seen here in Figure 2.

    TodoAdded
    Figure 2 - Todo Added
    Figure 2: Add a new todo.
    1. Click the empty circle next to a todo to complete it, or uncheck it to mark it as incomplete.
    2. Click the X to remove a todo.
    3. The OpenAPI link at the bottom of the page will open the OpenAPI 3.0 specification for the application.
    4. The Swagger UI link opens the embedded Swagger UI, which can be used to execute some of the RESTful endpoints directly.
    5. The Prometheus Metrics link leads to the Prometheus metrics endpoint, which would be scraped intermittently by Prometheus.
    6. The Health Check link opens the built-in health check exposed by Spring Boot.

    Go ahead and play around a bit to see it all in action. Don’t forget to come back here once you’re done! Use CTRL-C on your keyboard to stop the application once you’re done.

    Examine the internals

    The application is a full-featured Spring Boot application using the following capabilities:

    • Spring MVC for building a REST layer:
      • Open src/main/java/io/quarkus/todospringquarkus/TodoController.java to find the Spring MVC RESTful controller, exposing the various endpoints available to the user interface.
    • Spring Data JPA for defining relational entities as well as storing and retrieving them:
      • Open src/main/java/io/quarkus/todospringquarkus/TodoEntity.java to find the Java Persistence API (JPA) entity, representing the relational table for storing the todos.
      • Open src/main/java/io/quarkus/todospringquarkus/TodoRepository.java to find the Spring Data JPA Repository, exposing all of the create, read, update, and delete operations for the TodoEntity.
    • Spring Boot Actuators for providing operational capabilities, including health checks and metrics gathering.
    • SpringDoc OpenAPI 3 for generating and exposing RESTful API information as well as the embedded Swagger UI endpoint.
    • Prometheus Micrometer Registry for exposing metrics to Prometheus.
    • Open src/main/resources/META-INF/resources to find the user interface components used.

    Configuration

    Open src/main/resources/application.properties to find the application configuration:

    spring.jpa.hibernate.ddl-auto=create-drop
    spring.datasource.url=jdbc:postgresql://localhost:5432/tododb
    spring.datasource.username=todo
    spring.datasource.password=todo
    
    springdoc.api-docs.path=/openapi
    springdoc.swagger-ui.path=/swagger-ui
    
    management.endpoints.web.exposure.include=prometheus,health
    

    Open src/main/resources/import.sql to find some SQL that will pre-populate the database table with an initial set of data:

    INSERT INTO todo(id, title, completed) VALUES (0, 'My first todo', 'true');
    

    That’s it Not a lot of code here for a fully-functional application that’s a whole lot more than “Hello World!”

    The Magic

    One hard requirement for this migration has been chosen: The application’s source code cannot be modified in any way.

    Are you ready for the magic trick? Return to the command line and run the command ./.mvnw clean spring-boot:run. If you want to use Gradle instead of Maven, first switch to the gradle branch (git checkout gradle) and run the command ./.gradlew clean bootRun.

    The first thing you’ll notice is the Quarkus banner and startup messaging instead of the Spring Boot one:

    __  ____  __  _____   ___  __ ____  ______
     --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
     -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
    --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
    INFO  [io.quarkus] (Quarkus Main Thread) todo-spring-quarkus 0.0.1-SNAPSHOT on JVM (powered by Quarkus 2.0.0.Final) started in 2.748s. Listening on: http://localhost:8080
    INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
    INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, kubernetes, micrometer, narayana-jta, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, smallrye-openapi, spring-data-jpa, spring-di, spring-web, swagger-ui]
    

    Wait, what just happened? The application is now a Quarkus application and no longer a Spring Boot application? That's madness!

    Want proof? Go back to your browser window (http://localhost:8080 in case you closed it) and reload the page. The same user interface is there and is completely functional. Click on all the various links at the bottom of the page. They’re all completely functional as they were before.

    Also, note the startup time. The Quarkus version starts up in almost half the amount of time given the exact same codebase (4.392s for Spring vs. 2.748s for Quarkus in the shown examples). That's Supersonic, Subatomic, Java!

    Nothing changed, so how did all this happen? A good magician doesn’t reveal his or her secrets!

    The Madness

    All good magicians use sleight-of-hand to distract his or her audience when performing a trick. There are a few things that weren’t shown yet in this post that are hidden from the naked eye. Did you notice anything suspicious? Let’s take a closer look at how the trick was done.

    Execution

    Look very closely at the commands you used to run the application:

    Using Maven:

    Spring Boot: ./mvnw clean spring-boot:run

    Quarkus: ./.mvnw clean spring-boot:run

    Using Gradle:

    Spring Boot: ./gradlew clean bootRun

    Quarkus: ./.gradlew clean bootRun

    Notice any differences? A different executable (.mvnw/.gradlew) is used in the Quarkus version. If you open those files and examine them closely, you’ll notice some trickery going on:

    .mvnw

    #!/bin/sh
    ./mvnw clean quarkus:dev -Pquarkus
    

    .gradlew

    #!/bin/sh
    ./gradlew -Pprofile=quarkus $@
    

    We used some sleight-of-hand to disguise the actual commands used to start the application. More on Gradle and Maven profiles later.

    Configuration

    Are you saying that Quarkus knows how to read and understand the Spring Boot configuration inside src/main/resources/application.properties? Well, no. Remember, this is a magic trick. A magician never tells the complete truth. What wasn’t shown in the example above is the Quarkus-specific configuration. It was hidden further down in the file.

    If you re-open src/main/resources/application.properties and scroll all the way to the bottom (around line 58) you’ll see some additional configuration:

    quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/tododb
    quarkus.datasource.username=todo
    quarkus.datasource.password=todo
    quarkus.datasource.metrics.enabled=true
    quarkus.hibernate-orm.database.generation=drop-and-create
    quarkus.hibernate-orm.sql-load-script=import.sql
    
    quarkus.swagger-ui.always-include=true
    quarkus.swagger-ui.path=/swagger-ui
    quarkus.micrometer.export.prometheus.path=/actuator/prometheus
    quarkus.smallrye-health.root-path=/actuator/health
    quarkus.smallrye-openapi.path=/openapi
    

    This configuration is similar to the Spring Boot configuration at the top of the file. One thing it does do, however, is to re-define the paths for the Prometheus and the health probe endpoints, so they match the paths of the Spring Boot actuator endpoints. This allows the Prometheus Metrics and Health Check links at the bottom of the screen to work without any changes to the user interface.

    Pretty sneaky, huh?

    Dependencies

    As you can imagine, there are lots of dependencies that need to be changed, added, or updated. Luckily, Quarkus provides many Spring compatibility extensions:

    • Quarkus extension for Spring Dependency Injection
    • Quarkus extension for Spring Web
    • Quarkus extension for Spring Data JPA
    • Quarkus extension for Spring Boot Properties

    Simply swapping some of the Spring Boot dependencies for the Quarkus ones will go a long way. There are a few other capabilities, such as Prometheus metrics, OpenAPI documentation, Swagger UI integration, and health checks that need other dependencies added. Lucky for us again, Quarkus has these capabilities as well:

    • Quarkus extension for OpenAPI and Swagger UI
    • Quarkus extension for MicroProfile Health
    • Quarkus extension for Micrometer Metrics

    Now you’re probably thinking to yourself, “But I didn’t make ANY changes whatsoever. All I did was run a Maven or Gradle command, and the entire application ran as a Quarkus application instead of a Spring Boot application. How can that be?”

    Build setup

    The build file is where all the magic happens. The build tool you are using determines how the dependency resolution magic actually happens, though.

    Maven

    On the main branch, open the pom.xml file. You’ll immediately notice the build file is broken into multiple Maven profiles, spring and quarkus, with the spring profile being the default profile. These profiles define which dependencies and build plugins are included when you run either ./mvnw clean spring-boot:run or ./mvnw -Pquarkus clean quarkus:dev (which is disguised using some redirection in the aforementioned .mvnw script).

    Gradle

    Gradle, on the other hand, does not have the concept of a profile. You’ll notice a handful of .gradle files in the gradle branch of the project:

    • build-common.gradle
      • Contains any common build logic applicable to both versions of the application, including setting the groupId/version of the produced artifact as well as artifact repository definitions.
    • build-spring.gradle
      • Contains all build logic, plugins, and dependencies specific to the Spring version of the application.
    • build-quarkus.gradle
      • Contains all build logic, plugins, and dependencies specific to the Quarkus version of the application. build-quarkus.gradle even introduces Spring Boot’s bootRun task, re-mapping it to the quarkusDev task from the Quarkus Gradle plugin.
    • settings.gradle
      • Where the magic actually happens! The settings.gradle file looks for a Gradle project property called profile:
        • If this property isn’t found, it defaults the value to spring.
        • It then sets the project’s build file, either build-spring.gradle or build-quarkus.gradle, as the main build file to use.

    In general, this is a pretty good pattern to use if you need Gradle to simulate the capabilities of Maven profiles.

    Application Main Class

    It was mentioned at the beginning of this post that we wanted to perform the migration without changing a single line of code. Every Spring Boot application needs to have an “application” class that contains a main method and is annotated with @SpringBootApplication. In our project, src/main/java/io/quarkus/todospringquarkus/TodoApplication.java is that class.

    Quarkus does not require such a class, nor do any of the Quarkus Spring compatibility extensions provide resolution for the @SpringBootApplication annotation nor the SpringApplication class referenced in this class.

    So, what gives? We didn’t make any code changes whatsoever, yet those classes seem to resolve just fine in Quarkus.

    You’ll notice a peculiar comment in both pom.xml (for Maven)/build-quarkus.gradle (for Gradle), right above the dependency declaration for the dependency org.springframework.boot:spring-boot-autoconfigure:

    This dependency is a hack for TodoApplication.java, which isn't required for Quarkus. Point of demo is to NOT have any code changes.
    

    This is the key to this part of the trick. This dependency allows both Spring Boot and Quarkus to resolve these classes at build time. The dependency is declared optional in Maven/compileOnly in Gradle, meaning it will never be included in the application binary the Quarkus build produces. It will be included in the binary the Spring Boot build produces because all of the other spring-boot-starter-* dependencies also depend on it, so it's included transitively.

    Wrap Up

    You saw in this post how to take an existing Spring Boot application and run it on Quarkus without making a single change to the code. Was the method magic or madness? Maybe a little bit of both? It's up to you to decide for yourself.

    This post showed one way to migrate an application that is more than “Hello World” from Spring Boot to Quarkus with a hard requirement of not changing a single line of source code. It isn’t at all intended to represent the only potential migration path. Furthermore, there may be times when an application uses some library or API where there isn’t a Quarkus equivalent. In those cases, there may be code changes or some refactoring necessary.

    A big thanks to Eric Murphy. He is the original author of the application and came up with the magic trick idea.

    References

    All source code for this post can be found here. The main branch contains the Maven version, and the gradle branch contains the Gradle version. The application source code is the same on both branches.

    Last updated: August 7, 2023

    Related Posts

    • Why should I choose Quarkus over Spring for my microservices?

    • Quarkus: A quick-start guide to the Kubernetes-native Java stack

    • Mandrel: A specialized distribution of GraalVM for Quarkus

    Recent Posts

    • Storage considerations for OpenShift Virtualization

    • Upgrade from OpenShift Service Mesh 2.6 to 3.0 with Kiali

    • EE Builder with Ansible Automation Platform on OpenShift

    • How to debug confidential containers securely

    • Announcing self-service access to Red Hat Enterprise Linux for Business Developers

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue