Skip to content

Migrating Legacy Spring Applications to Quarkus

Migrating Legacy Spring Applications to Quarkus

Section titled “Migrating Legacy Spring Applications to Quarkus”

In this article, I’ll share my experience migrating legacy Spring applications to Quarkus, a modern Java framework designed for cloud-native applications.

During my time at PT. Asuransi Jiwa Astra, we successfully migrated several legacy Spring applications to Quarkus. The key benefits we observed were:

  1. Faster startup times - From minutes to seconds
  2. Lower memory footprint - Significant reduction in heap usage
  3. Better developer experience - Live reload and faster builds
  4. Cloud-native optimizations - Built for containers and serverless

Before starting the migration, we conducted a thorough assessment:

Terminal window
# Analyze existing Spring dependencies
mvn dependency:tree
# Identify custom components and configurations
find . -name "*.xml" -o -name "*.properties" | grep -E "(spring|application)"

We didn’t attempt a big-bang migration. Instead, we used a phased approach:

  1. Start with new microservices using Quarkus
  2. Gradually migrate non-critical components
  3. Migrate business-critical services last

Spring and Quarkus have different dependency ecosystems. Here’s how we handled it:

Before (Spring):

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

After (Quarkus):

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>

Spring Configuration:

@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}

Quarkus Equivalent:

@ApplicationScoped
public class DataSourceProducer {
@Produces
@ApplicationScoped
public DataSource dataSource() {
return new HikariDataSource();
}
}

Spring Controller:

@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}

Quarkus Resource:

@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
@GET
@Path("/{id}")
public User getUser(@PathParam Long id) {
return userService.findById(id);
}
}
ApplicationSpring BootQuarkusImprovement
User Service45s3.2s93% faster
Order Service78s5.1s93% faster
Integration Service120s8.7s93% faster
ApplicationSpring BootQuarkusReduction
User Service512MB128MB75%
Order Service768MB192MB75%
Integration Service1GB256MB75%

Quarkus provides many extensions. Only include what you need:

<!-- Good: Only what you need -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<!-- Avoid: Including everything -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bom</artifactId>
</dependency>

For maximum performance, use GraalVM native compilation:

Terminal window
# Build native executable
./mvnw package -Pnative
# Results in much faster startup and lower memory

Update your testing approach for Quarkus:

@QuarkusTest
public class UserResourceTest {
@Test
public void testGetUser() {
given()
.pathParam("id", 1)
.when()
.get("/api/users/{id}")
.then()
.statusCode(200)
.body("name", equalTo("John Doe"));
}
}

Avoid having both Spring and Quarkus dependencies in the same application.

Remember to update your Maven/Gradle scripts for Quarkus-specific commands.

Set up proper monitoring to validate performance improvements.

Migrating from Spring to Quarkus requires careful planning and execution, but the benefits are substantial. Our migration resulted in:

  • 93% faster startup times
  • 75% reduction in memory usage
  • Improved developer productivity
  • Better cloud-native capabilities

The key is to approach migration incrementally and thoroughly test each component before moving to the next.


This migration was successfully completed for multiple production services at PT. Asuransi Jiwa Astra.