Spring to Quarkus Without the Rewrite Headache: Your Guide to 10 Essential Bridge Extensions
Discover how Quarkus compatibility layers let you reuse existing Spring patterns for DI, Web, Data, and more, enabling a smooth, incremental migration.
Quarkus has rapidly gained traction in the Java ecosystem, particularly for cloud-native applications, microservices, and serverless functions. Its promise of supersonic subatomic Java – meaning incredibly fast startup times and low memory consumption – combined with a focus on developer joy makes it an attractive alternative or complement to established frameworks like Spring Boot.
For developers heavily invested in the Spring Framework, the thought of switching might seem daunting. Years of experience, familiar APIs, and existing codebases represent significant investments. Fortunately, the Quarkus community, through the Quarkiverse (a central hub for Quarkus extensions), has developed a suite of compatibility extensions specifically designed to ease this transition.
These extensions act as bridges, allowing you to leverage your existing Spring knowledge and even reuse significant portions of your code while gradually adopting Quarkus' native features. They provide familiar annotations and patterns, reducing the initial learning curve and enabling an incremental migration strategy.
This article shows you the top 10 Quarkus extensions that are particularly valuable for Spring developers, providing a first look at how they get you started on the path to Quarkus.
Prerequisites:
Basic understanding of Java, Maven or Gradle.
Familiarity with core Spring Framework concepts (DI, Web, Data, Security, etc.).
An IDE (like VS Code with Quarkus Tools, IntelliJ IDEA).
GraalVM installed (Optional, for native compilation).
Setting Up a Project:
You can create a new Quarkus project and add these extensions using the Quarkus CLI, Maven plugin, or the code.quarkus.io website. For example, using Maven:
mvn io.quarkus.platform:quarkus-maven-plugin:3.21.0:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=spring-migration-example \
-Dextensions="resteasy-reactive-jackson, quarkus-spring-web, quarkus-spring-di, quarkus-spring-data-jpa, hibernate-orm-panache, jdbc-postgresql"
cd spring-migration-example
1. Quarkus Extension for Spring DI API (quarkus-spring-di)
This extension provides compatibility with Spring's core Dependency Injection (DI) annotations.
Why it's valuable: DI is fundamental to Spring. This extension allows you to keep using familiar annotations like @Component
, @Service
, @Repository
, @Configuration
, and crucially, @Autowired
for injecting dependencies, and @Value
for injecting configuration properties. Quarkus maps these annotations to its underlying ArC dependency injection container (based on CDI).
package org.acme.services;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service // Spring's annotation
public class GreetingService {
@Value("${greeting.message:Hello}") // Spring's @Value
private String message;
public String greet(String name) {
return message + " " + name + " from Quarkus via Spring DI!";
}
}
package org.acme.resources;
import org.acme.services.GreetingService;
import org.springframework.beans.factory.annotation.Autowired;
// Using Jakarta EE path parameters for compatibility with JAX-RS/RESTEasy Reactive
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello-spring-di")
public class GreetingResource {
@Autowired // Spring's annotation
private GreetingService service;
@GET
@Path("/{name}")
@Produces(MediaType.TEXT_PLAIN)
public String hello(@PathParam("name") String name) {
return service.greet(name);
}
}
Add to application.properties
:
greeting.message=Hi
Benefit: Reduces the immediate need to learn CDI annotations (@ApplicationScoped
, @Inject
, etc.), allowing teams to migrate components with minimal changes to their DI wiring.
2. Quarkus Extension for Spring Web API (quarkus-spring-web)
Quarkus Spring Web API Extension enables the use of Spring Web annotations for defining REST controllers.
Why it's valuable: Migrating REST endpoints is often a primary concern. This extension supports annotations like @RestController
, @RequestMapping
, @GetMapping
, @PostMapping
, @PutMapping
, @DeleteMapping
, @PathVariable
, @RequestParam
, and @RequestBody
. Quarkus translates these into its native JAX-RS layer (typically RESTEasy Reactive).
package org.acme.controllers;
import org.springframework.web.bind.annotation.*; // Spring annotations
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController // Spring annotation
@RequestMapping("/api/items") // Spring annotation
public class ItemController {
private final Map<Long, String> items = new ConcurrentHashMap<>();
private long counter = 0;
@GetMapping("/{id}") // Spring annotation
public String getItem(@PathVariable Long id) { // Spring annotation
return items.getOrDefault(id, "Not Found");
}
@PostMapping
public Long addItem(@RequestBody String item) { // Spring annotation
long id = ++counter;
items.put(id, item);
System.out.println("Added item: " + id + " -> " + item);
return id;
}
}
Benefit: Allows direct reuse or minimal modification of existing Spring REST controllers, significantly speeding up the migration of API layers.
3. Extension for Spring Data API (quarkus-spring-data-jpa)
Quarkus provides a compatibility layer for Spring Data JPA repositories in the form of the spring-data-jpa extension.
Why it's valuable: Spring Data JPA simplifies data access with its repository abstraction and derived query methods. This extension allows you to define repository interfaces extending Spring Data's JpaRepository
or CrudRepository
, and Quarkus will implement them using Hibernate ORM and Panache behind the scenes.
First, define an entity (using Jakarta Persistence annotations, which Spring Data JPA also uses):
package org.acme.data;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class Product {
@Id
@GeneratedValue
public Long id;
public String name;
public double price;
// Constructors, getters, setters (or use public fields with Panache)
}
Now, the Spring Data JPA repository:
package org.acme.data;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; // Optional, but good practice
import java.util.List;
@Repository // Spring annotation
public interface ProductRepository extends JpaRepository<Product, Long> { // Spring Data interface
// Derived query method - familiar Spring Data pattern
List<Product> findByNameContainingIgnoreCase(String name);
List<Product> findByPriceGreaterThan(double price);
}
You can then @Autowired
this repository into your services or controllers. Remember to configure your datasource in application.properties
.
Benefit: Preserves familiar and powerful data access patterns. Avoids rewriting data repositories and query logic during the initial migration phases.
4. Quarkus Extension for Spring Security API (quarkus-spring-security)
This extension offers a compatibility layer for certain Spring Security concepts, so a Quarkus application can leverage the well-known Spring Security annotations to define authorizations on RESTful services using roles.
Why it's valuable: Securing applications is critical. While Quarkus has its own robust security framework, this extension provides a bridge by recognizing annotations like @Secured
or integrating with configurations that resemble Spring Security principles, easing the transition for authorization logic. Note: This compatibility layer is less comprehensive than others; Quarkus security fundamentals differ significantly from Spring Security's filter chain approach.
package org.acme.services;
import org.springframework.security.access.annotation.Secured; // Requires spring-security dependency conceptually
import org.springframework.stereotype.Service;
@Service
public class AdminService {
// Quarkus Security needs configuration to understand roles
// This annotation signals intent compatible with Spring Security's usage
@Secured("ADMIN")
public String performAdminTask() {
return "Admin task performed successfully.";
}
}
You would still need to configure Quarkus's security mechanisms (e.g., properties security, Elytron, OIDC) to define users, roles, and how authentication occurs. The extension helps map the authorization intent expressed via common annotations.
Benefit: Allows developers to maintain familiar annotation-based authorization checks (@Secured
, potentially others depending on extension evolution) while integrating with Quarkus's underlying security mechanisms. Caution: Deeper migration will likely involve learning native Quarkus security.
5. Accessing application properties with Spring Boot properties API (quarkus-spring-boot-properties)
This extension supports Spring Boot's property file formats (application.properties
, application.yml
) and type-safe property binding.
Why it's valuable: Configuration management is key. This extension allows Quarkus to read Spring Boot's standard property files and supports @ConfigurationProperties
for binding configuration values to structured objects.
Define a configuration properties class:
package org.acme.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
// Using jakarta.validation for constraints
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
// Maps properties prefixed with "app.info"
@ConfigurationProperties(prefix = "app.info")
public class AppInfoProperties {
@NotBlank // Use Jakarta Bean Validation
private String name;
private String description;
@Positive
private int version;
// Getters and Setters needed for binding
// ...
}
Add to application.properties
:
app.info.name=My Spring-Compatible Quarkus App
app.info.description=An application demonstrating Spring compatibility
app.info.version=1
Inject and use it:
@Path("/app-info")
public class AppInfoResource {
@Autowired // Can use Spring DI
private AppInfoProperties appInfo;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getAppInfo() {
return "App: " + appInfo.getName() + ", Version: " + appInfo.getVersion();
}
}
Benefit: Enables reuse of existing Spring Boot configuration files and patterns, simplifying environment setup and configuration management during migration.
6. Reading properties from Spring Cloud Config Server (quarkus-spring-cloud-config-client)
With this extentsion Quarkus is enabled to act as a client to a Spring Cloud Config Server.
Why it's valuable: Many organizations use Spring Cloud Config Server for centralized configuration management across microservices. This extension allows new Quarkus applications (or migrated ones) to integrate seamlessly into this existing infrastructure without requiring changes to the central config server.
Add the extension and configure the connection in application.properties
:
# Enable the config client
quarkus.spring-cloud-config.enabled=true
# URL of the Spring Cloud Config Server
quarkus.spring-cloud-config.uri=http://your-config-server:8888
# Optional: Application name and profile(s) used for lookup
quarkus.application.name=my-quarkus-app
quarkus.profile=prod
# Optional: Authentication details if the server is secured
# quarkus.spring-cloud-config.username=user
# quarkus.spring-cloud-config.password=pass
Quarkus will fetch configuration from the specified server on startup, merging it with local application.properties
. Properties fetched from the config server take precedence.
Benefit: Critical for integrating Quarkus applications into existing Spring Cloud ecosystems. Avoids infrastructure duplication or complex configuration workarounds.
7. Quarkus Extension for Spring Cache API (quarkus-spring-cache)
Supports Spring's caching annotations for declarative caching and enables Quarkus application to leverage the well known Spring Cache annotations for application data caching for their Spring beans.
Why it's valuable: Caching is essential for performance. This extension recognizes Spring's @Cacheable
, @CachePut
, and @CacheEvict
annotations, allowing you to maintain your existing caching logic without modification. Quarkus wires these annotations to its underlying cache implementation (often Caffeine).
package org.acme.services;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class ExpensiveDataService {
// Cache results of this method in a cache named "data-cache"
@Cacheable("data-cache")
public String getData(String key) {
System.out.println(">>> Performing expensive operation for key: " + key);
try {
TimeUnit.SECONDS.sleep(2); // Simulate delay
} catch (InterruptedException e) { Thread.currentThread().interrupt(); }
return "Data for " + key;
}
// Update the cache "data-cache" with the result of this method
@CachePut(value = "data-cache", key = "#key")
public String updateData(String key, String value) {
System.out.println(">>> Updating data for key: " + key);
// ... actual update logic ...
return value;
}
// Evict (remove) the entry for 'key' from "data-cache"
@CacheEvict(value = "data-cache", key = "#key")
public void evictData(String key) {
System.out.println(">>> Evicting data for key: " + key);
}
}
You need to add a cache implementation dependency like quarkus-cache
.
Benefit: Preserves caching behavior defined declaratively using familiar Spring annotations, preventing the need to rewrite caching logic during migration.
8. Quarkus Extension for Spring Scheduling API (quarkus-spring-scheduled)
This extension enables the use of Spring's @Scheduled
annotation for defining scheduled tasks.
Why it's valuable: Many applications have background tasks running on timers or cron schedules. This extension allows you to keep using Spring's @Scheduled
annotation with its familiar cron
, fixedRate
, and fixedDelay
attributes. Quarkus integrates this with its own scheduler.
package org.acme.tasks;
import io.quarkus.logging.Log;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class ScheduledTasks {
// Runs every 10 seconds
@Scheduled(fixedRate = 10000)
public void reportTimeFixedRate() {
Log.info("Spring Fixed Rate Task :: Time is " + LocalDateTime.now());
}
// Runs every minute, based on cron expression
@Scheduled(cron = "0 * * * * ?")
public void reportTimeCron() {
Log.info("Spring Cron Task :: Time is " + LocalDateTime.now());
}
}
You also need to add the quarkus-scheduler
dependency.
Benefit: Allows direct migration of scheduled tasks defined with @Scheduled
without rewriting the scheduling logic or learning Quarkus's native @Scheduled
syntax initially.
9. Validation with Hibernate Validator (quarkus-hibernate-validator)
This extension integrates Jakarta Bean Validation (formerly JSR 380/Bean Validation 2.0) capabilities into Quarkus, using Hibernate Validator as the underlying engine. Similar to what Spring developers are familiar with in Spring's validation framework.
Why it's valuable: Data validation is a cornerstone of robust applications. Spring developers heavily rely on Bean Validation annotations (@NotNull
, @Size
, @Email
, @Pattern
, etc.) integrated into the framework, often triggering validation automatically in REST controllers using @Valid
. quarkus-hibernate-validator
provides the exact same standard validation mechanism. There's no need to learn a new validation API or rewrite existing validation constraints defined on your model objects. It seamlessly plugs into REST endpoints (JAX-RS/RESTEasy Reactive and Spring Web compatible endpoints) and allows for validation at service method boundaries.
Define a bean with standard Jakarta Bean Validation annotations:
package org.acme.models;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class UserInput {
@NotBlank(message = "Username cannot be blank")
@Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters")
public String username;
@NotBlank(message = "Email cannot be blank")
@Email(message = "Email should be valid")
public String email;
// Getters and setters or public fields
}
Now, trigger validation in a REST endpoint using @Valid
. This example uses JAX-RS annotations, but the @Valid
principle is the same if using the quarkus-spring-web
extension.
package org.acme.resources;
import org.acme.models.UserInput;
import jakarta.inject.Inject; // Or use @Autowired with quarkus-spring-di
import jakarta.validation.Valid;
import jakarta.validation.Validator; // Can inject validator for programmatic validation
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
@Inject // Example of injecting the validator itself if needed
Validator validator;
@POST
public Response createUser(@Valid UserInput user) { // @Valid triggers automatic validation
// If validation fails, Quarkus automatically returns a 400 Bad Request
// with details about the constraint violations.
// If validation passes, this code is executed.
System.out.println("User input is valid: " + user.username);
// ... logic to create user ...
return Response.status(Response.Status.CREATED).entity(user).build();
}
// Example of programmatic validation (less common for endpoint input)
// public Response someOtherMethod(UserInput user) {
// Set<ConstraintViolation<UserInput>> violations = validator.validate(user);
// if (!violations.isEmpty()) {
// // Handle violations
// return Response.status(Response.Status.BAD_REQUEST).entity(violations).build();
// }
// // ... proceed ...
// }
}
Benefit: Provides immediate familiarity and allows direct reuse of existing validation annotations and logic. Spring developers don't need to adapt to a different validation framework, ensuring consistency and leveraging the widely adopted Jakarta Bean Validation standard right away in Quarkus. The automatic integration with REST layers via @Valid
mirrors the convenient behavior found in Spring Boot.
10. Extension for Spring Data REST (quarkus-spring-data-rest)
This is a compatibility extension for Spring Data REST, which automatically exposes REST endpoints for Spring Data repositories.
Why it's valuable: Spring Data REST offers a convention-over-configuration approach to quickly create HATEOAS-compliant REST APIs based on your repositories. If your application relies on this auto-generation using annotations like @RepositoryRestResource
, this extension aims to provide similar functionality within Quarkus.
Conceptual overview, relies on quarkus-spring-data-jpa
package org.acme.data;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource; // Spring Data REST annotation
// Assuming Product entity exists from example #3
@RepositoryRestResource(collectionResourceRel = "products", path = "products") // Spring Data REST annotation
public interface ProductRestRepository extends JpaRepository<Product, Long> {
// No methods needed for basic CRUD if using auto-exposure
}
Adding this extension (along with -jpa
and -web
) would instruct Quarkus to attempt to expose CRUD endpoints under /products
based on the ProductRepository
.
Benefit: Maintains the rapid API generation capabilities for applications heavily leveraging Spring Data REST, reducing the need to manually create basic CRUD controllers during migration.
A Pragmatic Path Forward
These Quarkus Spring compatibility extensions are powerful tools for lowering the barrier to entry for Spring developers. They enable a pragmatic, incremental migration strategy:
Start Familiar: Begin by migrating components using the Spring compatibility extensions, leveraging existing code and knowledge.
Integrate: Get the application running on Quarkus, benefiting immediately from faster startup and lower resource consumption.
Optimize Gradually: As your team becomes more comfortable with Quarkus, you can selectively refactor parts of the application to use native Quarkus APIs (CDI, RESTEasy Reactive, Panache, Quarkus Security, etc.) where it makes sense to unlock further performance gains or utilize specific Quarkus features.
These extensions are not intended as a permanent replacement for learning Quarkus fundamentals but as an accelerator for adoption. They include Quarkus' pragmatic approach, meeting developers where they are and providing a smooth on-ramp to the world of supersonic subatomic Java. By using these bridges, you can confidently explore and adopt Quarkus without discarding existing, valuable experience.