From Spring Boot to Quarkus
Tags: kotlin, springboot, quarkus, cloudnative
Published: 2020-11-15 16:15:11

Last week, I [migrated a sample Spring application to Micronaut](https://blog.frankel.ch/spring-to-micronaut/). This week, I did the same for [Quarkus](https://quarkus.io/).

## Common changes

Spring Boot and Micronaut both offer a parent POM. Quarkus favors using a <abbr title="Bill-Of-Material">BOM</abbr>.

```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'.

```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:

```kotlin
@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:

```kotlin
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:

```
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.driver=org.h2.Driver
quarkus.datasource.jdbc.url=jdbc:h2:mem:test
```

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

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

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

```
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](https://smallrye.io/), which is an implementation of [MicroProfile](https://microprofile.io/) "(but not limited to) for the Cloud". Smallrye also provides a `/health` endpoint. It just requires to add a dependency:

```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 <abbr title="Context and Dependency Injection">CDI</abbr> 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.

```kotlin
@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 <abbr title="Ahead-Of-Time">AOT</abbr> 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](ajavageek/spring-to-micronaut/tree/quarkus).

**To go further:**

* [Quarkus website](https://quarkus.io/)
* [Quarkus Spring](https://quarkus.io/guides/spring-di)
* [Quarkus Smallrye](https://quarkus.io/guides/microprofile-health)
* [Quarkus Flyway](https://quarkus.io/guides/flyway)
* [Spring2quarkus — Spring Boot to Quarkus Migration](https://dzone.com/articles/spring2quarkus-spring-boot-to-quarkus-migration)


_Originally published at [A Java Geek](https://blog.frankel.ch/spring-to-quarkus/) on November 15<sup>th</sup> 2020_