/ SPRING BOOT, QUARKUS, CLOUD NATIVE

From Spring Boot to Quarkus

Last week, I migrated a sample Spring application to Micronaut. This week, I did the same for Quarkus.

Common changes

Spring Boot and Micronaut both offer a parent POM. Quarkus favors using a BOM.

pom.xml
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.3.5.RELEASE</version>
  <relativePath/>
</parent>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-bom</artifactId>
      <version>1.9.2.Final</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Migrating the application

Quarkus provides dependencies that have the same interfaces and annotations as Spring. They map one to one. The next step is just a matter of replacing Spring’s dependencies with Quarkus'.

pom.xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-spring-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-spring-web</artifactId>
</dependency>

To launch the application requires just a single main() function, just as Micronaut:

@SpringBootApplication
class SpringToMicronautApplication

fun main(args: Array<String>) {
  runApplication<SpringToMicronautApplication>(*args)
}

fun main(args: Array<String>) {
  Quarkus.run(*args)
}

There’s a glitch on the controller side. Quarkus doesn’t read the @PathVariable variable name from the bytecode. The name needs to be explicitly set in the annotation:

fun getOne(@PathVariable("id") id: Long): Optional<Person> = repo.findById(id)

The beauty of Quarkus is that no further steps are required!

More migration steps on the data layer

Though the database driver is on the classpath, it’s necessary to configure the database:

application.properties
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.driver=org.h2.Driver
quarkus.datasource.jdbc.url=jdbc:h2:mem:test
Update on November 16th

I never update my posts and prefer to write another one. In that case, I really missed something and the update is just the day after.

Thanks Gunnar Morling for the poke.

Just as for Micronaut, Quarkus allow neither to create the schema nor to initialize the data, but has a Flyway integration.

Quarkus relies on Hibernate’s capability to create the schema from JPA entities. Additionally, you can configure to execute a SQL script at startup:

quarkus.hibernate-orm.sql-load-script=data.sql
quarkus.hibernate-orm.database.generation=create

Flyway integration

Alternatively, Quarkus also offers a Flyway integration.

pom.xml
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-flyway</artifactId>
</dependency>

Moreover, you need to set the quarkus.flyway.migrate-at-start property:

application.properties
quarkus.flyway.migrate-at-start=true

Migrating the actuator

There’s no actuator per se in the Quarkus universe. But it provides integration with SmallRye, which is an implementation of MicroProfile "(but not limited to) for the Cloud". Smallrye also provides a /health endpoint. It just requires to add a dependency:

pom.xml
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-smallrye-health</artifactId>
</dependency>

By default, the endpoint is available at the root. To mirror Spring’s URL is a configuration knob away:

quarkus.smallrye-health.root-path=/actuator/health

In Spring applications, the /beans endpoint queries the Spring context and return the list of beans it contains. I found no counterpart in Smallrye nor Microprofile. Of course, a CDI bean and a Spring Boot bean are different. Let’s return the list of CDI beans.

In CDI, the BeanManager is the object that’s able to do that. The endpoint is a simple controller.

@RestController
class BeanController(private val manager: BeanManager) {              (1)

  @GetMapping(path = ["/actuator/beans"])                             (2)
  fun getAll(): Iterable<Bean<*>> = manager.getBeans(Any::class.java) (3)
}
1 Inject the BeanManager
2 Configure the endpoint URL
3 List the beans and return them

Conclusion

Within the scope of this sample application, migrating from Spring Boot to Quarkus is straightforward. The API dependencies play a big role in that. Except for explicitly setting the parameter name, it’s seamless.

I was a bit surprised there’s no AOT compilation as with Micronaut. I assume GraalVM native image creation leverages some mechanism to make reflection explicit.

The real gap lies in the actuator but it’s possible to implement missing endpoints manually.

The complete source code for this post can be found on Github.
Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Currently working for Hazelcast. Also double as a trainer and triples as a book author.

Read More
From Spring Boot to Quarkus
Share this