Start / Cheat Sheets

Cheat Sheet

Spring Boot nach Quarkus übertragen — Cheat Sheet

Stand: · Quarkus für Spring-Boot-Teams

QuarkusSpring BootCDIPanache

Das Blatt für den Moment, in dem eine Spring-Boot-Klasse offen ist und die Quarkus-Entsprechung fehlt: Annotationen, Konfigurationsschlüssel, Abfrageformen. Die maßgebliche und stets aktuelle Doku steht auf quarkus.io — hier steht die Auswahl, die beim Übertragen wirklich gebraucht wird.

Die zweite Hälfte des Seminars — Tests, Security, Messaging, Betrieb und der Migrationsweg — liegt auf dem Blatt Quarkus testen, betreiben und migrieren.

Wofür welche Antwort spricht

Die Entscheidung ist keine Frameworkfrage, sondern eine Abwägung mit zwei möglichen Ausgängen.

Spricht für QuarkusSpricht für Spring Boot
Quarkus ist als Plattform bereits gesetztViel Spring-spezifische Infrastruktur im Code
Stark skalierende, häufig startende LastNicht unterstützte Spring-Bibliotheken im Einsatz
Kubernetes- und Native-Image-AnforderungenKein messbarer Betriebsnutzen aus dem Umbau
Jakarta- und MicroProfile-APIs erwünschtTeamkompetenz und Prozesse klar auf Spring

Spring Boot bietet selbst AOT, Native Images und Testcontainers — allgemeine Argumente tragen die Entscheidung deshalb nicht.

Modul: Warum Quarkus für Spring-Boot-Teams

Starter und Extension

Der eine Unterschied, aus dem die meisten anderen folgen: Quarkus verlagert Arbeit vom Start in den Build.

Spring-Boot-StarterQuarkus Extension
WirkzeitpunktStart der AnwendungBuild der Anwendung
MechanismusAuto-Configuration, BedingungenBuild Steps und Build Items
ErgebnisBeans im Contexterzeugter Bytecode im Artefakt
Fehlerzeitpunkterster StartBuildlauf

Daraus folgt unmittelbar: @Conditional und die übrigen Conditional-Annotationen werden ignoriert, weil die Injektion schon zur Bauzeit aufgelöst ist.

Modul: Spring Boot und Quarkus im Architekturvergleich

Vertraute Handgriffe, andere Stelle

Spring BootQuarkus
application.ymlapplication.properties
spring.profiles.activequarkus.profile
@ConfigurationProperties@ConfigMapping
@Value@ConfigProperty
spring-boot-starter-webquarkus-rest-jackson

YAML ist über eine eigene Extension nachrüstbar, der Standardweg sind aber Properties. Profile heißen dev, test und prod:

quarkus.http.port=8080
%dev.quarkus.http.port=8181
%test.quarkus.log.level=WARN
# QUARKUS_HTTP_PORT=9090 sticht die Datei
quarkus create app de.hco:pfandkreis --extension='rest-jackson'
quarkus extension add hibernate-orm-panache
quarkus extension list --installable
quarkus dev          # Dev UI: http://localhost:8080/q/dev-ui/

Modul: Das Quarkus-Projekt aufsetzen · Warum Quarkus für Spring-Boot-Teams

Dependency Injection: Spring zu CDI

SpringQuarkusAnmerkung
@Autowired@InjectListen über io.quarkus.arc.All
@Qualifier@Named
@Value@ConfigPropertykeine Expression Language
@Component, @Service, @Repository@SingletonSingleton als Vorgabe
@Configuration@ApplicationScoped
@Bean@Produces
@ComponentScannicht unterstütztScan zur Bauzeit
@Importnicht unterstütztkein CDI-Gegenstück

Die Kompatibilitätsschicht kommt über quarkus extension add spring-di und ist als Zwischenschritt gedacht, nicht als Ziel. @Autowired(required=false) deckt sie nicht ab — dort tritt Instance an seine Stelle.

@Service                          // Spring-Kompatibilität
public class PfandRechner {
    @Autowired RueckgabeRepository repo;
}

@ApplicationScoped                // CDI
public class PfandRechner {
    @Inject RueckgabeRepository repo;
}

Modul: Dependency Injection von Spring zu CDI

REST-Annotationen

Spring WebJakarta REST
@RestController@Path auf der Klasse
@GetMapping, @PostMapping@GET, @POST mit @Path
@PathVariable@PathParam oder @RestPath
@RequestParam@QueryParam oder @RestQuery
@RequestHeader@HeaderParam oder @RestHeader
@RequestBodyder Methodenparameter selbst
@ResponseStatus@APIResponse und Response-Status

Die Spring-Kompatibilität deckt außerdem @MatrixVariable, @CookieValue, @ExceptionHandler und @RestControllerAdvice ab — von letzterer allerdings ausschließlich die Exception-Handler-Funktion. Der Application Context wird dabei nie gestartet; die Annotationen dienen nur als Metadaten.

@Path("/becher")
class BecherResource {
  @GET @Path("/{id}")
  Becher holen(@RestPath String id) { ... }

  @POST
  Response anlegen(@Valid RueckgabeDto dto) { ... }
  // Verstoß → 400 mit { "title": "Constraint Violation", "violations": [...] }
}

Das Antwortformat bestimmt ein eigener ExceptionMapper mit @Provider vollständig; er löst damit den eingebauten Mapper der Validierung ab. OpenAPI liefert quarkus-smallrye-openapi unter /q/openapi, die Swagger UI unter /q/swagger-ui — letztere nur in dev und test.

Modul: REST APIs mit Quarkus entwickeln

Konfigurationsquellen und ihr Ordinal

Die höhere Zahl gewinnt.

QuelleOrdinal
System-Properties400
Umgebungsvariablen300
Datei .env im Arbeitsverzeichnis295
config/application.properties neben dem Start260
application.properties im Classpath250
META-INF/microprofile-config.properties100

Für den Schlüssel foo.BAR.baz sucht Quarkus zusätzlich nach foo_BAR_baz und FOO_BAR_BAZ. Im .env-Format heißt %dev.quarkus.http.port entsprechend _DEV_QUARKUS_HTTP_PORT.

@ConfigMapping(prefix = "pfandkreis")
public interface PfandConfig {
  int fristTage();              // → pfandkreis.frist-tage
  BigDecimal pfandProBecher();  // → pfandkreis.pfand-pro-becher
}
quarkus.datasource.username=${DB_USER}
quarkus.datasource.password=${DB_PASS:leer}   # Doppelpunkt = Vorgabewert
quarkus.config.profile.parent=common

Modul: Konfiguration und Profile übertragen

Persistence: Spring Data zu Panache

Was die Kompatibilitätsschicht für Spring Data JPA ausdrücklich nicht abdeckt:

Nicht unterstütztKonsequenz
QueryDSLAbfragen neu schreiben
QueryByExampleExecutorKriterien explizit formulieren
JpaSpecificationExecutorSpecifications entfallen
Native Queries in @Queryanders ausführen
@LockSperrverhalten neu lösen
Future als RückgabetypSignatur ändern

Ebenfalls nicht abgedeckt: eigene Basis-Repositories und Entity-State über EntityInformation.

Spring DataPanache
findAll()listAll()
findById(id)findById(id), findByIdOptional(id)
findByStelle(s)list("stelle", s)
countByStelle(s)count("stelle", s)
save(e)persist(), persistAndFlush()
Pageablepage(Page.of(7, 25))

Die verkürzten Abfragen sind HQL-Fragmente, keine eigene Sprache: list("stelle", s) wird zu from Becher where stelle = ?1.

@ApplicationScoped
public class BecherRepo implements PanacheRepository<Becher> {
  List<Becher> offen(String stelle) {
    return list("stelle = ?1 and zurueck is null", Sort.by("ausgabe"), stelle);
  }
}
quarkus.flyway.migrate-at-start=true
quarkus.hibernate-orm.database.generation=none   # nicht beides zugleich

Modul: Persistence mit Hibernate ORM und Panache

Transaktionen und Querschnittsfunktionen

VarianteVerhalten
REQUIREDstartet eine Transaktion oder nutzt die vorhandene
REQUIRES_NEWsetzt die laufende aus und startet eine neue
MANDATORYFehler, wenn keine Transaktion läuft
SUPPORTSnutzt eine vorhandene, läuft sonst ohne
NOT_SUPPORTEDsetzt eine laufende Transaktion aus
NEVERFehler, wenn eine Transaktion läuft

Ungeprüfte Ausnahmen lösen ein Rollback aus; rollbackOn und dontRollbackOn korrigieren das gezielt. Global gilt quarkus.transaction-manager.default-transaction-timeout.

@Transactional
@TransactionConfiguration(timeout = 30)
public void abrechnen(String stelle) { ... }

QuarkusTransaction.requiringNew().run(() -> protokoll.schreiben(eintrag));

@CacheResult(cacheName = "pfandsaetze")
public BigDecimal satz(@CacheKey String becherTyp) { ... }

@Scheduled(cron = "{pfandkreis.abrechnung.cron}")
void abrechnungslauf() { ... }

Was in Spring über Aspekte gelöst war, übernehmen Interceptors. Der Caffeine-Cache ist prozesslokal, und der einfache Scheduler läuft in jeder Instanz — für Ausführung genau einmal im Cluster ist Quartz der vorgesehene Weg.

Modul: Transaktionen, Validierung und Querschnittsfunktionen

Typische Fallen

  • Eine Build-Time-Property im Container gesetzt — sie bleibt wirkungslos, weil der Wert schon im Bytecode steht.
  • Eine nur reflektiv genutzte Bean verschwindet, weil ArC sie als ungenutzt entfernt; @Unremovable hält sie.
  • Beans aus einer Fremdbibliothek ohne Jandex-Index bleiben unsichtbar.
  • Bei Mehrdeutigkeit gibt es keinen Rückfall auf den Bean-Namen wie in Spring — @Named ist Pflicht.
  • Eine Spring-Bibliothek kompiliert sauber und tut zur Laufzeit nichts, weil ihr Laufzeitmodell fehlt.
  • HttpServletRequest gibt es nur mit quarkus-resteasy, nicht mit quarkus-rest.
  • Das Fehlerformat ändert sich bei der Migration und bricht die Clients still — der eingebaute Validierungs-Mapper überschreibt gern das eigene Format.
  • @Value-Ausdrücke werden übernommen, obwohl @ConfigProperty keine Expression Language kennt.
  • application-prod.properties sticht die Prozent-Einträge unbemerkt.
  • Eine Specification steckt tief in der Suchfunktion und fällt erst spät auf; @Lock verschwindet dabei lautlos und mit ihm die Kollisionssicherheit.
  • Hibernate erzeugt das Schema, während Flyway migriert — beides zugleich aktiviert kollidiert.
  • Eine geprüfte Ausnahme löst kein Rollback aus; ohne rollbackOn bleibt es dabei.
  • Der nächtliche Lauf startet nach dem Skalieren dreimal gleichzeitig.

Zum Seminar Quarkus für Spring-Boot-Teams