Spring Boot Basic – Tag 9 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting


JAX-RS in Spring Boot

📍 Deine Position im Kurs

TagThemaStatus
✅ 1Erste REST APIAbgeschlossen
✅ 2Spring Container & DIAbgeschlossen
✅ 3@Controller & Thymeleaf BasicsAbgeschlossen
✅ 4Thymeleaf Forms & MVC-PatternAbgeschlossen
✅ 5Konfiguration & LoggingAbgeschlossen
✅ 6DI & AOP im DetailAbgeschlossen
✅ 7Scopes in SpringAbgeschlossen
✅ 8WebSocketsAbgeschlossen
→ 9JAX-RS in Spring Boot👉 DU BIST HIER!
10Integration & AbschlussMorgen!

Modul: Spring Boot Basic (10 Arbeitstage)
Dein Ziel: REST APIs mit Java Standards bauen!


📋 Voraussetzungen

Du brauchst:

  • ✅ Tag 1-8 abgeschlossen
  • ✅ REST APIs mit Spring MVC verstehen (@RestController, @GetMapping)
  • ✅ JSON Serialisierung verstehen
  • ✅ HTTP-Methoden kennen (GET, POST, PUT, DELETE)

Tag 8 verpasst? → Hier geht’s zum Blogbeitrag Tag 8


⚡ Was dich heute erwartet

Bisher: Du hast REST APIs mit Spring MVC gebaut:

@RestController
@RequestMapping("/api/persons")
public class PersonApiController {
    
    @GetMapping
    public List<Person> getAllPersons() {
        return personService.getAllPersons();
    }
}

Das funktioniert perfekt! Aber es ist Spring-spezifisch.

Heute: Lernst du JAX-RS – den Java Standard für REST APIs!

@Path("/persons")
public class PersonResource {
    
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Person> getAllPersons() {
        return personService.getAllPersons();
    }
}

Der Unterschied:

  • Spring MVC = Spring-spezifisch (@RestController, @GetMapping)
  • JAX-RS = Java Standard (@Path, @GET) – funktioniert in jedem Java EE Server!

Warum ist das wichtig?

  • 🏢 Enterprise-Welt nutzt JAX-RS
  • 📦 Portabilität – Code läuft auf Tomcat, WildFly, GlassFish, Payara
  • 🔄 Migration – von Java EE zu Spring Boot (und zurück!)
  • 📚 Standard-Wissen – gilt für ganze Java-Welt

Hier kannst du dir die Sourcen von heute auf Github herunterladen.

🎯 Dein Lernpfad heute

Du arbeitest heute in mehreren aufbauenden Schwierigkeitsstufen. Arbeite in deinem eigenen Tempo durch die Schritte:

🟢 Grundlagen (Schritte 1-3)

Was du lernst:

  • JAX-RS verstehen – was ist das?
  • Unterschied Spring MVC vs JAX-RS
  • Jersey in Spring Boot integrieren
  • Dependencies einrichten
  • Erste JAX-RS Resource erstellen
  • @Path, @GET, @POST, @PUT, @DELETE

Ziel: Du verstehst JAX-RS und hast eine funktionierende Resource

Dauer: ~3 Stunden


🟡 Professional (Schritte 4-5)

Was du lernst:

  • @Produces und @Consumes (Content Negotiation)
  • Parameter-Annotations (@PathParam, @QueryParam)
  • Response-Objekte und HTTP-Status
  • Exception Handling mit @Provider
  • Custom Exceptions und ExceptionMapper

Ziel: Production-Ready JAX-RS API mit Error Handling

Dauer: ~3 Stunden


🔵 Bonus: Enterprise Features (Schritt 6)

Was du lernst:

  • Spring MVC vs JAX-RS Side-by-Side
  • Migration-Patterns von Java EE zu Spring Boot
  • Wann JAX-RS, wann Spring MVC?
  • Beide Ansätze parallel nutzen
  • Best Practices für Enterprise

Ziel: Migration-Ready sein und beide Welten beherrschen

Dauer: ~2 Stunden


💻 Los geht’s!

🟢 GRUNDLAGEN (Schritte 1-3)

Schritt 1: Was ist JAX-RS? (45 Min)

1.1 JAX-RS verstehen

JAX-RS = Jakarta RESTful Web Services (früher: Java API for RESTful Web Services)

Was ist das?

  • Ein Java Standard für REST APIs
  • Teil von Jakarta EE (früher Java EE)
  • Vendor-neutral – nicht an Spring gebunden!
  • Implementierungen: Jersey, RESTEasy, Apache CXF

Die Analogie:

JDBC = Standard für Datenbank-Zugriff
  → Implementierungen: MySQL Driver, PostgreSQL Driver

JAX-RS = Standard für REST APIs
  → Implementierungen: Jersey, RESTEasy, CXF

1.2 Spring MVC vs JAX-RS – Der Vergleich

Vergleichstabelle:

FeatureSpring MVCJAX-RS
StandardSpring-spezifischJava/Jakarta Standard
Annotation Style@GetMapping, @PostMapping@GET, @POST
Path Definition@RequestMapping(„/api/persons“)@Path(„/persons“)
Content Typeproduces/consumes in Mapping@Produces, @Consumes
Parameter Binding@RequestBody, @PathVariableAutomatisch, @PathParam
PortabilitätNur Spring BootJeder Jakarta EE Server

1.3 Warum JAX-RS lernen?

1. Enterprise-Standard:

Banken, Versicherungen, große Konzerne:
→ Nutzen oft Java EE/Jakarta EE
→ JAX-RS ist der Standard dort
→ Wenn du in diesen Firmen arbeitest: JAX-RS-Wissen essentiell!

2. Portabilität:

JAX-RS Code läuft auf:
✅ Tomcat, WildFly, GlassFish, Payara, Open Liberty, Spring Boot

Spring MVC Code läuft auf:
✅ Spring Boot
❌ WildFly (ohne Spring)

3. Migration:

Firma hat Java EE Apps mit JAX-RS
→ Migration zu Spring Boot
→ Du musst JAX-RS verstehen!

4. Job-Markt:

"Java Backend Developer" Stellenanzeigen:
→ 60% fordern JAX-RS Kenntnisse
→ 80% fordern Spring Boot Kenntnisse
→ Wenn du BEIDES kannst: 2x wertvoller!

Schritt 2: Jersey in Spring Boot integrieren (1 Stunde)

2.1 Dependencies hinzufügen

pom.xml:

<dependencies>
    <!-- Existing dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Jersey (JAX-RS) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>
</dependencies>

Maven reload!

mvn clean install

2.2 Jersey Configuration erstellen

config/JerseyConfig.java:

package com.example.springbootbasic.config;

import com.example.springbootbasic.resource.PersonResource;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;

import jakarta.ws.rs.ApplicationPath;

@Configuration
@ApplicationPath("/api")  // Base-Path für alle JAX-RS Resources
public class JerseyConfig extends ResourceConfig {
    
    public JerseyConfig() {
        // Resources registrieren
        register(PersonResource.class);
    }
}

Was macht das:

@ApplicationPath("/api")
→ Alle JAX-RS Endpoints starten mit /api

register(PersonResource.class)
→ PersonResource wird verfügbar unter /api/persons

2.3 Erste JAX-RS Resource erstellen

resource/PersonResource.java:

package com.example.springbootbasic.resource;

import com.example.springbootbasic.model.Person;
import com.example.springbootbasic.service.PersonService;
import org.springframework.stereotype.Component;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;

@Component  // Spring Bean!
@Path("/persons")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PersonResource {
    
    private final PersonService personService;
    
    // Constructor Injection funktioniert!
    public PersonResource(PersonService personService) {
        this.personService = personService;
    }
    
    @GET
    public List<Person> getAllPersons() {
        return personService.getAllPersons();
    }
    
    @GET
    @Path("/{id}")
    public Response getPersonById(@PathParam("id") Long id) {
        Person person = personService.getPersonById(id);
        
        if (person == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        
        return Response.ok(person).build();
    }
    
    @POST
    public Response createPerson(Person person) {
        Person created = personService.createPerson(person);
        return Response
            .status(Response.Status.CREATED)
            .entity(created)
            .build();
    }
}

WICHTIG: @Component – damit Spring die Resource als Bean verwaltet!


Schritt 3: JAX-RS Annotations im Detail (1.5 Stunden)

3.1 @Path – URL-Mapping

Basis-Syntax:

@Path("/persons")              // /api/persons
public class PersonResource {
    
    @GET
    public List<Person> getAll() { }
    
    @GET
    @Path("/{id}")             // /api/persons/123
    public Person getById(@PathParam("id") Long id) { }
    
    @GET
    @Path("/{id}/address")     // /api/persons/123/address
    public Address getAddress(@PathParam("id") Long id) { }
}

3.2 HTTP-Method Annotations

Standard-Methods:

@GET     // Daten holen
@POST    // Daten erstellen
@PUT     // Daten aktualisieren (vollständig)
@DELETE  // Daten löschen
@PATCH   // Daten aktualisieren (teilweise)

Beispiel – CRUD vollständig:

@Path("/persons")
public class PersonResource {
    
    @GET
    public List<Person> getAll() {
        return personService.getAllPersons();
    }
    
    @POST
    public Response create(Person person) {
        Person created = personService.createPerson(person);
        return Response.status(Response.Status.CREATED)
                       .entity(created)
                       .build();
    }
    
    @PUT
    @Path("/{id}")
    public Person update(@PathParam("id") Long id, Person person) {
        return personService.updatePerson(id, person);
    }
    
    @DELETE
    @Path("/{id}")
    public Response delete(@PathParam("id") Long id) {
        personService.deletePerson(id);
        return Response.noContent().build();
    }
}

3.3 Testen!

# App starten
mvn spring-boot:run

# GET alle Personen (JAX-RS)
curl http://localhost:8080/api/persons

# GET eine Person (JAX-RS)
curl http://localhost:8080/api/persons/1

# POST neue Person (JAX-RS)
curl -X POST http://localhost:8080/api/persons \
  -H "Content-Type: application/json" \
  -d '{"firstname":"Tom","lastname":"Miller"}'

JAX-RS funktioniert! ✅


✅ Checkpoint Grundlagen

Kontrolliere:

  • [ ] Du verstehst was JAX-RS ist
  • [ ] Du kennst den Unterschied zu Spring MVC
  • [ ] Jersey Dependencies sind in pom.xml
  • [ ] JerseyConfig ist erstellt
  • [ ] PersonResource funktioniert
  • [ ] @Path, @GET, @POST verstanden
  • [ ] Du kannst die API testen

Alles ✅? Weiter zu 🟡 Professional!


🟡 PROFESSIONAL (Schritte 4-5)

Schritt 4: @Produces, @Consumes & Parameter (1.5 Stunden)

4.1 @Produces und @Consumes – Content Negotiation

@Produces – Was sendet die API:

@Path("/persons")
public class PersonResource {
    
    @GET
    @Produces(MediaType.APPLICATION_JSON)  // Nur JSON
    public List<Person> getAllJson() {
        return personService.getAllPersons();
    }
    
    @GET
    @Path("/flexible")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})  // Beides!
    public List<Person> getAllFlexible() {
        // Client entscheidet via Accept-Header
        return personService.getAllPersons();
    }
}

Client-Request:

# JSON anfordern
curl -H "Accept: application/json" http://localhost:8080/api/persons/flexible

# XML anfordern
curl -H "Accept: application/xml" http://localhost:8080/api/persons/flexible

4.2 Parameter-Annotations

@PathParam – URL-Parameter:

@GET
@Path("/{id}")
public Person getById(@PathParam("id") Long id) {
    return personService.getPersonById(id);
}

// URL: /api/persons/42
// id = 42

@QueryParam – Query-String-Parameter:

@GET
public List<Person> search(
        @QueryParam("firstname") String firstname,
        @QueryParam("lastname") String lastname,
        @QueryParam("page") @DefaultValue("0") int page,
        @QueryParam("size") @DefaultValue("10") int size) {
    
    return personService.search(firstname, lastname, page, size);
}

// URL: /api/persons?firstname=Max&page=0&size=20

@DefaultValue – Default-Werte:

@GET
public List<Person> getAll(
        @QueryParam("page") @DefaultValue("0") int page,
        @QueryParam("size") @DefaultValue("10") int size) {
    
    // Wenn nicht angegeben: page=0, size=10
    return personService.getAll(page, size);
}

4.3 Response-Objekte und HTTP-Status

Wichtige HTTP-Status-Codes:

CodeStatusVerwendung
200OKErfolgreiche GET/PUT-Operation
201CREATEDErfolgreiche POST-Operation
204NO_CONTENTErfolgreiche DELETE-Operation
400BAD_REQUESTUngültige Client-Daten
404NOT_FOUNDResource nicht gefunden

Beispiel:

@GET
@Path("/{id}")
public Response getById(@PathParam("id") Long id) {
    Person person = personService.getPersonById(id);
    
    if (person == null) {
        return Response
            .status(Response.Status.NOT_FOUND)
            .entity("Person not found with id: " + id)
            .build();
    }
    
    return Response
        .status(Response.Status.OK)
        .entity(person)
        .build();
}

Schritt 5: Exception Handling (1.5 Stunden)

5.1 ExceptionMapper erstellen

exception/PersonNotFoundExceptionMapper.java:

package com.example.springbootbasic.exception;

import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;

@Provider  // JAX-RS Exception Mapper
public class PersonNotFoundExceptionMapper 
        implements ExceptionMapper<PersonNotFoundException> {
    
    @Override
    public Response toResponse(PersonNotFoundException exception) {
        ErrorResponse error = new ErrorResponse(
            Response.Status.NOT_FOUND.getStatusCode(),
            exception.getMessage(),
            System.currentTimeMillis()
        );
        
        return Response
            .status(Response.Status.NOT_FOUND)
            .entity(error)
            .build();
    }
}

dto/ErrorResponse.java:

package com.example.springbootbasic.dto;

public class ErrorResponse {
    
    private int status;
    private String message;
    private long timestamp;
    
    public ErrorResponse() {
    }
    
    public ErrorResponse(int status, String message, long timestamp) {
        this.status = status;
        this.message = message;
        this.timestamp = timestamp;
    }
    
    // Getters and Setters
    public int getStatus() { return status; }
    public void setStatus(int status) { this.status = status; }
    
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
    
    public long getTimestamp() { return timestamp; }
    public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
}

5.2 Exception registrieren

@Configuration
@ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {
    
    public JerseyConfig() {
        register(PersonResource.class);
        register(PersonNotFoundExceptionMapper.class);  // Exception Mapper!
    }
}

5.3 Custom Exceptions nutzen

exception/PersonNotFoundException.java:

package com.example.springbootbasic.exception;

public class PersonNotFoundException extends RuntimeException {
    
    public PersonNotFoundException(Long id) {
        super("Person not found with id: " + id);
    }
}

Resource nutzt Exception:

@GET
@Path("/{id}")
public Person getById(@PathParam("id") Long id) {
    Person person = personService.getPersonById(id);
    
    if (person == null) {
        throw new PersonNotFoundException(id);  // Exception werfen!
    }
    
    return person;
}

Resultat:

curl http://localhost:8080/api/persons/999

# Response: 404 NOT FOUND
{
  "status": 404,
  "message": "Person not found with id: 999",
  "timestamp": 1697543210567
}

✅ Checkpoint Professional

Kontrolliere:

  • [ ] @Produces und @Consumes verstanden
  • [ ] @PathParam und @QueryParam funktionieren
  • [ ] Response-Objekte mit Status-Codes nutzen
  • [ ] ExceptionMapper erstellt
  • [ ] Custom Exceptions funktionieren
  • [ ] ErrorResponse als JSON zurückgeben

Alles ✅? Weiter zu 🔵 Enterprise Features!


🔵 BONUS: ENTERPRISE FEATURES (Schritt 6)

Schritt 6: Spring MVC vs JAX-RS – Side by Side (2 Stunden)

6.1 Code-Vergleich

FeatureSpring MVCJAX-RS
Class Annotation@RestController@Path
Base Path@RequestMapping(„/persons“)@Path(„/persons“)
GET Method@GetMapping@GET
POST Method@PostMapping@POST
Path Variable@PathVariable Long id@PathParam(„id“) Long id
Request Body@RequestBody PersonPerson (automatisch)
ResponseResponseEntityResponse

Ähnlichkeiten:

  • ✅ Beide nutzen Annotations
  • ✅ Beide unterstützen DI
  • ✅ Beide JSON-Serialisierung automatisch

Unterschiede:

  • ❌ Verschiedene Annotation-Namen
  • ❌ JAX-RS = Standard, Spring MVC = Framework-spezifisch

6.2 Wann was nutzen?

JAX-RS wenn:

  • Migration von Java EE zu Spring Boot
  • Portabilität wichtig (mehrere Server-Typen)
  • Enterprise-Standard gefordert
  • Team kennt Java EE

Spring MVC wenn:

  • Neue Spring Boot App
  • Spring-Ökosystem voll nutzen
  • Team kennt nur Spring
  • Keine Migrations-Anforderung

6.3 Beide parallel nutzen

// Spring MVC: /spring/persons
@RestController
@RequestMapping("/spring/persons")
public class PersonSpringController { }

// JAX-RS: /api/persons
@Path("/persons")
public class PersonJaxRsResource { }

// Beide laufen parallel! ✅

✅ Checkpoint Enterprise Features

Kontrolliere:

  • [ ] Du siehst den Unterschied Spring MVC vs JAX-RS
  • [ ] Du verstehst Vor- und Nachteile
  • [ ] Du weißt wann du was nutzt
  • [ ] Du kannst beide parallel nutzen
  • [ ] Du bist Migration-Ready!

Alles ✅? Du bist jetzt ein JAX-RS-Profi! 🎉

Hier sind noch ein paar sehr nützliche externe Links zum Thema JAX‑RS in Spring Boot – REST mit Java-Standards, die sich gut als Quellen oder weiterführende Materialien für dein Buchkapitel eignen:

  • „Create Jersey REST APIs with Spring Boot“ – Tutorial bei HowToDoInJava: zeigt Schritt-für-Schritt wie man JAX-RS (Jersey) neben Spring Boot einsetzt. HowToDoInJava
  • „Using JAX-RS with Spring Boot instead of MVC“ – Artikel bei DZone: erklärt die Integration von JAX-RS in Spring-Umgebung und worauf man achten sollte. DZone
  • „REST API: JAX-RS vs Spring“ – Artikel bei Baeldung: erläutert die Unterschiede zwischen JAX-RS und Spring MVC/REST-Ansatz. Baeldung on Kotlin
  • „Spring Boot JAX-RS Example“ – Java Code Geeks: Beispielprojekt mit Spring Boot + JAX-RS (z. B. mit Apache CXF) inkl. Konfigurationsdetails. Examples Java Code Geeks
  • „JAX-RS Spec and Implementations“ – Baeldung: erklärt, was JAX-RS eigentlich ist, als Spezifikation und welche Implementierungen existieren. Baeldung on Kotlin

🔥 Elyndras Real Talk:

Nova kam heute zu mir: „Elyndra, warum gibt es zwei Wege für REST APIs? Ist das nicht verwirrend?“

Die AutoTech Migration-Story

Das war 2021, mein viertes Jahr bei AutoTech. Wir hatten eine große Legacy-Anwendung auf WildFly mit JAX-RS.

Der Auftrag: „Wir wollen zu Spring Boot migrieren. Wie lange dauert das?“

Mein erster Gedanke: „Alles neu schreiben mit Spring MVC? 3 Monate!“

Code Sentinel: „Oder… du nutzt Jersey in Spring Boot. Dann läuft dein JAX-RS Code 1:1!“

Das Ergebnis:

Migration-Zeit: 5 Tage (statt 3 Monate)
Code-Änderungen: <5% (statt 100%)
Bugs: 2 kleine (statt viele neue)
✅ Production-Ready nach 1 Woche!

Nova war beeindruckt: „Das ist ja 12x schneller!“

„Exakt,“ sagte ich. „Das ist der Wert von Standards. JAX-RS ist portabel. Spring MVC ist Spring-spezifisch.“

Franz-Martin kam dazu: „Das ist der Grund warum ich beide Ansätze unterrichte. Die Realität nutzt beide. Banken migrieren von Java EE zu Spring Boot – sie brauchen Leute die BEIDE Welten kennen!“

Das ist die Realität: Beide Ansätze haben ihren Platz!


🆘 Troubleshooting

Problem 1: Jersey startet nicht

Lösung:

// JerseyConfig muss annotiert sein
@Configuration
@ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig { }

Problem 2: Resource nicht gefunden

Lösung:

// Resource registrieren
public JerseyConfig() {
    register(PersonResource.class);  // WICHTIG!
}

// UND @Component auf Resource
@Component
@Path("/persons")
public class PersonResource { }

❓ FAQ

Q: Wann JAX-RS, wann Spring MVC?
A: JAX-RS für Portabilität und Enterprise. Spring MVC für neue Spring Boot Apps.

Q: Kann ich beide in einer App nutzen?
A: Ja! /api/* mit JAX-RS, /spring/* mit Spring MVC. Beide parallel möglich!

Q: Funktioniert Spring DI in JAX-RS?
A: Ja, wenn die Resource @Component ist! Constructor Injection funktioniert.

Q: Was ist besser?
A: Keins ist „besser“ – beide haben Use Cases. JAX-RS = Standard & portabel, Spring MVC = Spring-integriert.


📅 Morgen: Tag 10 – Integration & Abschluss!

Letzter Kurstag:

  • Projekt-Integration aller Themen
  • Best Practices Zusammenfassung
  • Production Deployment
  • Was kommt danach?

Du hast 90% geschafft! 🎉


📚 Deine Fortschritts-Übersicht

TagStatus
✅ 1-8ABGESCHLOSSEN!
✅ 9ABGESCHLOSSEN!
→ 10Morgen!

Morgen ist der letzte Tag! 🚀


📥 Download & Ressourcen

Projekt: SpringBootJAXRS-v1.0.zip

Enthält:

  • Jersey Integration
  • PersonResource komplett
  • Exception Handling
  • Vergleich Spring MVC vs JAX-RS

Du kannst jetzt:

  • ✅ JAX-RS verstehen
  • ✅ Jersey in Spring Boot
  • ✅ REST APIs mit Java Standards
  • ✅ @Path, @GET, @POST nutzen
  • ✅ Exception Handling
  • ✅ Spring MVC vs JAX-RS
  • ✅ Migration-Ready sein! 🚀

Keep coding, keep learning! 💙


Tags: #SpringBoot #JAXRS #Jersey #REST #JavaStandards #Migration #Tag9

Autor

  • Elyndra Valen

    28 Jahre alt, wurde kürzlich zur Senior Entwicklerin befördert nach 4 Jahren intensiver Java-Entwicklung. Elyndra kennt die wichtigsten Frameworks und Patterns, beginnt aber gerade erst, die tieferen Zusammenhänge und Architektur-Entscheidungen zu verstehen. Sie ist die Brücke zwischen Junior- und Senior-Welt im Team.