Java Framework

Spring Boot

Spring Boot ist die meinungsstarke Schicht über dem Spring Framework, die einen Haufen von Konfigurationen in eine laufende Anwendung verwandelt: Starters, Auto-Konfiguration und ein eingebetteter Server.

advanced16 min readUpdated 16. Sept. 2026
PostController.java
java
// src/main/java/com/example/blog/PostController.java
@RestController
@RequestMapping("/api/posts")
public class PostController {

    private final PostService postService;

    public PostController(PostService postService) {
        this.postService = postService;
    }

    @GetMapping
    public List<PostResponse> list() {
        return postService.findAll();
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public PostResponse create(@Valid @RequestBody CreatePostRequest request) {
        return postService.create(request);
    }
}
Veröffentlicht
2014
Basiert auf
Spring Framework
Sprache
Java, Kotlin, Groovy
Server
Eingebetteter Tomcat, Jetty oder Netty
Stil
Convention und Auto-Konfiguration
Version
3.x

Warum es wichtig ist

Warum Spring Boot die JVM dominiert

In Minuten startklar

Ein Starter und eine Main-Methode liefern dir eine produktionsreife Anwendung mit eingebettetem Server, sodass du nichts deployen musst, bevor du Code schreibst.

Auto-Konfiguration

Spring Boot analysiert den Classpath und konfiguriert die gefundenen Beans. Füge einen Datenbanktreiber hinzu, und eine DataSource erscheint, bereit zum Einsatz.

Ein kohärentes Ökosystem

Spring Data, Security, Batch, Integration und Cloud teilen dasselbe Programmiermodell, sodass sich Kenntnisse über die gesamte Plattform hinweg übertragen lassen.

Das Gesamtbild

Auto-Config, DI, Starters

Starters ziehen kohärente Abhängigkeiten hinein, die Auto-Konfiguration verdrahtet sie anhand sinnvoller Standardwerte und der Container injiziert die Objekte, die deine Klassen benötigen.

Auto-Konfiguration

Bootstrap

Bedingte Konfigurationen lesen aus, was sich auf dem Classpath befindet, und liefern Standardwerte, die du über Properties statt über Code überschreibst.

Annotierte Web-Layer

Expose

Annotationen mappen HTTP-Methoden und Pfade auf Java-Methoden, und Message Converter wandeln Objekte automatisch in JSON um.

Spring Data JPA

Persist

Repository-Interfaces bieten CRUD, Query-Methoden und Paging ohne handgeschriebene Implementierungen.

HTML5 auf einen Blick

Die Spring Boot Toolbox

Eingebetteter Server

Tomcat, Jetty oder Netty laufen innerhalb des Prozesses als einfache Java-Bibliothek.

Starters

spring-boot-starter-web bindet den gesamten Web-Stack in einer einzigen Abhängigkeit ein.

Annotationen

@RestController, @GetMapping und @RequestBody beschreiben die API.

Dependency Injection

Der Application Context erstellt und verdrahtet Beans via Constructor Injection.

Spring Data JPA

Repository-Interfaces ersetzen Boilerplate-Code für den Datenzugriff.

Actuator

Health-, Metrics- und Info-Endpunkte sind nur eine Abhängigkeit entfernt.

Eine kurze Geschichte

Von Spring XML zu einer einzeiligen Main-Methode

  1. 2012

    Spring Boot wird angekündigt

    Das Spring-Team präsentiert einen Weg, Anwendungen ohne das XML zu bootstrappen, das um das Framework herum gewachsen war.

    12
  2. 2014

    Spring Boot 1.0

    Das erste GA-Release macht Starters, Auto-Konfiguration und einen eingebetteten Server zum Standardweg für den Bau von Spring-Apps.

    14
  3. 2018

    Spring Boot 2.0

    Spring 5 bringt WebFlux, einen neu geschriebenen Actuator und erstklassigen Kotlin-Support.

    18
  4. 2022

    Spring Boot 3.0

    Java 17 wird zur Baseline, der jakarta-Namespace ersetzt javax und GraalVM Native Images werden ausgereift.

    22
  5. 2024

    Spring Boot 3.4

    Strukturiertes Logging, verbesserte Observability und aktualisierte Dependency-Baselines halten die Plattform aktuell.

    24
  6. Heute

    Der Standard-JVM-Stack

    Spring Boot ist der angenommene Startpunkt für neue Java-Services, von Monolithen bis hin zu cloud-nativen Microservices.

    Heute

Der vollständige Leitfaden

Spring Boot: Alles was Sie wissen müssen

Was ist Spring Boot?

Spring Boot ist eine Schicht über dem Spring Framework, die den Konfigurationsaufwand eliminiert, der früher in Spring erforderlich war. Anstatt XML-Dateien, Servlet-Konfigurationen und eines Containers für das Deployment zu benötigen, schreiben Sie eine Klasse mit einer main-Methode, fügen eine Starter-Dependency hinzu und erhalten eine laufende Anwendung mit einem eingebetteten Server.

Drei Kernkonzepte erledigen die Hauptarbeit. Starters bündeln die Abhängigkeiten für eine bestimmte Funktionalität in einer einzigen Zeile. Auto-configuration analysiert den Classpath und konfiguriert Beans auf Basis sinnvoller Standardwerte. Der embedded server bedeutet, dass die Anwendung ein normaler Java-Prozess ist – es muss nichts separat installiert oder bereitgestellt werden.

Das Ergebnis ist, dass Spring Boot zum Standardweg für die Entwicklung von JVM-Services geworden ist – von kleinen internen APIs bis hin zu großen Microservice-Flotten –, während die volle Leistungsfähigkeit des Spring-Ökosystems im Hintergrund erhalten bleibt.

Das Spring-Ökosystem in einer Minute

Spring ist nicht nur eine einzelne Library, sondern eine ganze Familie, die ein gemeinsames Programmiermodell nutzt:

  • Spring Framework — der Core-Container, Dependency Injection und die Web-Layer.
  • Spring Data — Repositories für SQL- und NoSQL-Speicher.
  • Spring Security — Authentifizierung und Autorisierung.
  • Spring Batch / Integration — Batch-Verarbeitung und Messaging.
  • Spring Cloud — Konfiguration, Discovery und Resilience für verteilte Systeme.

Da diese Komponenten gemeinsam entwickelt wurden, kann ein Projekt sie nach und nach implementieren, ohne die Art und Weise der Programmierung ändern zu müssen. Diese Konsistenz ist der eigentliche Grund, warum Teams sich auf Spring standardisieren.

Starter und Dependency-Management

Ein Starter ist eine kuratierte Sammlung von Dependencies, die harmonisch zusammenarbeiten. Du deklarierst die gewünschte Funktionalität und nicht die einzelnen Jars.

<!-- pom.xml -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Das Gradle-Äquivalent ist ebenso kurz:

dependencies {
  implementation "org.springframework.boot:spring-boot-starter-web"
  implementation "org.springframework.boot:spring-boot-starter-data-jpa"
  implementation "org.springframework.boot:spring-boot-starter-validation"
  runtimeOnly "org.postgresql:postgresql"
}

Das Parent-POM oder das dependency-management-Plugin legt die kompatiblen Versionen für dich fest. Überschreibe eine Version nur, wenn du einen triftigen Grund hast, und überlasse den Rest der Plattform.

Die Application-Klasse und der embedded Server

Jede Spring Boot Anwendung beginnt hier. Diese eine Klasse genügt, um einen Webserver zu starten.

package com.example.blog;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BlogApplication {
    public static void main(String[] args) {
        SpringApplication.run(BlogApplication.class, args);
    }
}

@SpringBootApplication kombiniert drei Annotationen: @Configuration, @EnableAutoConfiguration und @ComponentScan. Das Component Scanning beginnt im Package dieser Klasse und sucht in den darunterliegenden Packages. Aus diesem Grund sollte die Main-Klasse an der Wurzel Ihrer Package-Struktur liegen.

Das Ausführen von ./mvnw spring-boot:run startet einen embedded Tomcat auf Port 8080. Sie können Jetty oder Netty durch eine Änderung der Dependencies austauschen, ohne dass dies Auswirkungen auf den Anwendungscode hat.

Die annotierte Web-Layer

Spring MVC mappt HTTP-Requests mithilfe von Annotationen auf Methoden. @RestController kombiniert @Controller und @ResponseBody, sodass zurückgegebene Objekte von Jackson in JSON serialisiert werden.

@RestController
@RequestMapping("/api/posts")
public class PostController {

    private final PostService service;

    public PostController(PostService service) {
        this.service = service;
    }

    @GetMapping
    public List<PostResponse> list(
            @RequestParam(defaultValue = "0") int page) {
        return service.findPage(page);
    }

    @GetMapping("/{id}")
    public PostResponse get(@PathVariable Long id) {
        return service.findById(id);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public PostResponse create(@Valid @RequestBody CreatePostRequest request) {
        return service.create(request);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        service.delete(id);
    }
}

@PathVariable liest aus der URL, @RequestParam aus dem Query-String und @RequestBody deserialisiert den Payload. Halten Sie Controller schlank: Sie übersetzen HTTP in einen Aufruf eines Services und übersetzen das Ergebnis zurück. Die Domänenlogik gehört in die Service-Layer.

Dependency Injection und der Application Context

Der Application Context ist der Container, der Objekte (sogenannte Beans) erstellt und deren Abhängigkeiten bereitstellt. Sie legen fest, was eine Klasse benötigt; Spring entscheidet, wie diese erstellt wird.

@Service
public class PostService {

    private final PostRepository repository;
    private final Clock clock;

    public PostService(PostRepository repository, Clock clock) {
        this.repository = repository;
        this.clock = clock;
    }

    @Transactional
    public PostResponse create(CreatePostRequest request) {
        var post = new Post(request.title(), request.body(), clock.instant());
        return PostResponse.from(repository.save(post));
    }
}

Stereotype-Annotationen markieren die zu verwaltenden Klassen: @Component für generische Beans, @Service für Domain-Services, @Repository für den Datenzugriff und @RestController für Web-Endpoints. Für alles, was Sie nicht annotieren können – etwa eine Drittanbieter-Klasse oder einen konfigurierten Client – deklarieren Sie dies in einer @Configuration Klasse.

@Configuration
class ClockConfig {

    @Bean
    Clock clock() {
        return Clock.systemUTC();
    }
}

Bevorzugen Sie überall die Constructor Injection. Sie macht Abhängigkeiten explizit, ermöglicht final Felder und erlaubt es Ihnen, die Klasse in einem einfachen Unit-Test zu instanziieren, ohne Spring starten zu müssen.

Spring Data JPA und die Datenbank

Entities bilden Java-Objekte auf Tabellen ab, und Repositories ermöglichen den Datenzugriff ohne eine eigene Implementierung.

@Entity
@Table(name = "posts")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 120)
    private String title;

    @Column(nullable = false)
    private String body;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Author author;

    protected Post() {
    }

    public Post(String title, String body, Author author) {
        this.title = title;
        this.body = body;
        this.author = author;
    }

    // getters and domain methods
}
public interface PostRepository extends JpaRepository<Post, Long> {

    List<Post> findByPublishedTrueOrderByCreatedAtDesc();

    @Query("select p from Post p join fetch p.author where p.id = :id")
    Optional<Post> findWithAuthor(Long id);
}

Spring Data leitet eine Query aus dem Methodennamen ab, sodass findByPublishedTrueOrderByCreatedAtDesc keinen Body benötigt. Verwende @Query, wenn der abgeleitete Name unleserlich würde, und join fetch, um das Lazy-Loading N+1-Problem zu vermeiden. Setze ddl-auto in der Produktion auf validate und verwalte das Schema mit Flyway oder Liquibase.

Konfiguration mit application.yml und Profiles

Die Konfiguration befindet sich in src/main/resources und kann durch Umgebungsvariablen überschrieben werden – genau das ermöglicht es, dass dieselbe jar überall läuft.

# src/main/resources/application.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/blog
    username: blog
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    open-in-view: false
server:
  port: 8080

Profiles ermöglichen es, dass ein Build je nach Umgebung unterschiedlich reagiert.

# src/main/resources/application-prod.yml
spring:
  datasource:
    url: ${DATABASE_URL}
logging:
  level:
    root: warn

Aktiviere ein Profile mit --spring.profiles.active=prod oder der Umgebungsvariable SPRING_PROFILES_ACTIVE. @ConfigurationProperties bindet eine Gruppe von Einstellungen an eine typisierte Klasse, was sicherer ist, als @Value-Annotationen über die gesamte Codebasis zu verteilen.

Validierung

Jakarta Bean Validation Constraints an einem Request-Objekt werden erzwungen, wenn der Controller-Parameter mit @Valid annotiert ist.

public record CreatePostRequest(
        @NotBlank @Size(max = 120) String title,
        @NotBlank String body
) {
}

Ein @RestControllerAdvice zentralisiert die Fehlerantwort, sodass jeder Endpunkt das gleiche Format zurückgibt.

@RestControllerAdvice
public class ApiExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> onValidation(MethodArgumentNotValidException ex) {
        return ex.getBindingResult().getFieldErrors().stream()
                .collect(Collectors.toMap(
                        FieldError::getField,
                        FieldError::getDefaultMessage,
                        (first, second) -> first));
    }
}

Validieren Sie an der Grenze (Boundary) und lassen Sie die Domain ihren Inputs vertrauen. In Kombination mit einem konsistenten Exception-Handler entfernt dies repetitive if (input == null)-Prüfungen aus jedem Service.

Spring Security im Überblick

Spring Security ist eine Filter-Chain vor Ihrer Anwendung. In modernem Spring Boot wird die Konfiguration über ein Bean anstelle von XML vorgenommen.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/actuator/health").permitAll()
                        .anyRequest().authenticated())
                .oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()));
        return http.build();
    }
}

Dieses einzelne Bean erfordert für jeden Endpunkt ein gültiges JWT, mit Ausnahme des Health-Checks. Regeln auf Methoden-Ebene mittels @PreAuthorize ermöglichen eine feingranulare Autorisierung dort, wo sie benötigt wird. Sicherheit ist ein komplexes Thema – planen Sie ein, die Dokumentation sorgfältig zu lesen, bevor Sie Funktionen im Internet veröffentlichen.

Actuator: Health und Metrics

Durch das Hinzufügen des Actuator-Starters werden betriebliche Endpunkte kostenlos bereitgestellt.

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always
curl localhost:8080/actuator/health

/actuator/health lässt sich in Liveness- und Readiness-Probes integrieren, und /actuator/metrics liefert Statistiken zu JVM, HTTP und der Datenquelle. Stellen Sie nur das bereit, was Sie wirklich benötigen, und sichern Sie den Rest ab – diese Endpunkte geben viele Details über ein laufendes System preis.

Testing

@SpringBootTest startet den Applikationskontext und bildet die Basis für Integrationstests. MockMvc testet den Web-Layer, ohne einen echten Port zu öffnen.

@SpringBootTest
@AutoConfigureMockMvc
class PostControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void listsPosts() throws Exception {
        mockMvc.perform(get("/api/posts"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].title").value("Hello"));
    }
}

Für ein schnelleres Feedback solltest du den Kontext auf den zu testenden Layer einschränken und die Collaborators mocken.

@WebMvcTest(PostController.class)
class PostControllerSliceTest {

    @Autowired
    private MockMvc mockMvc;

    @MockitoBean
    private PostService service;

    @Test
    void returnsNotFound() throws Exception {
        given(service.findById(1L)).willThrow(new PostNotFoundException(1L));
        mockMvc.perform(get("/api/posts/1"))
                .andExpect(status().isNotFound());
    }
}

Nutze Slices für Controller und Repositories und behalte den vollständigen Kontext für einige wenige End-to-End-Tests vor. Testcontainers bietet dir eine echte Datenbank in den Tests, ohne dass die Persistenz gemockt werden muss.

Build und Deployment

Der Build-Prozess erzeugt ein ausführbares Fat Jar, das deine Klassen, Abhängigkeiten und den eingebetteten Server enthält.

./mvnw clean package
java -jar target/blog-0.0.1-SNAPSHOT.jar

Für Container erstellen Buildpacks ein optimiertes Image ohne Dockerfile.

./mvnw spring-boot:build-image

Die Konfiguration erfolgt über die Umgebungsvariablen, sodass dasselbe Artefakt unverändert von Staging nach Production überführt werden kann. Füge ein Dockerfile nur dann hinzu, wenn du eine Kontrolle benötigst, die Buildpacks nicht bieten, und bevorzuge einen Multi-Stage-Build, um das Image klein zu halten.

Best Practices

  • Paketierung nach Features, sodass jede Domäne ihren eigenen Controller, Service und Repository besitzt.
  • Nutze Constructor Injection und halte Felder final.
  • Gib aus Controllern DTOs anstelle von JPA-Entities zurück.
  • Validiere Request-Objekte mit @Valid und behandle Fehler in einem einzigen @RestControllerAdvice.
  • Halte ddl-auto auf validate und verwalte Schema-Änderungen mit Flyway oder Liquibase.
  • Externalisiere die Konfiguration und nutze Profile, anstatt für jede Umgebung neu zu bauen.
  • Füge Actuator hinzu, exponiere dann aber nur die Endpunkte und sichere diese, die du tatsächlich benötigst.

Häufige Fehler

  • Field Injection mit @Autowired, wodurch Abhängigkeiten versteckt werden und das Testen erschwert wird.
  • Direkte Serialisierung von Entities, was zu Leaks von Lazy-Loading oder internen Spalten führt.
  • Business-Logik in den Controllern ansammeln, anstatt diese an Services zu delegieren.
  • Verwendung von ddl-auto: update in der Produktion, was zu Abweichungen vom tatsächlichen Schema führt.
  • Starten des gesamten Contexts in jedem Unit-Test, was die Testsuite verlangsamt.
  • Öffnen aller Actuator-Endpunkte für das öffentliche Internet.
  • Hinzufügen von Startern, die nicht benötigt werden, was die Startup-Zeit verlängert.

Wie geht es weiter?

Spring Boot vermittelt Konzepte wie Dependency Injection, Auto-Configuration und Layered Design – Ideen, die weit über Java hinausgehen. Wenn Ihnen das Modul- und Injection-Modell gefällt, setzt NestJS dieses in TypeScript um, während Express die minimalistische Alternative aufzeigt. Die annotierte Web-Layer ist letztlich eine REST API, und da die meisten Anwendungen Daten in einem relationalen Store speichern, ist PostgreSQL der natürliche nächste Schritt.

Injizieren von Abhängigkeiten

Constructor Injection macht erforderliche Collaborators explizit, hält Felder final und ermöglicht es, die Klasse zu testen, ohne Spring zu starten.

Bevorzugt
@Service
public class PostService {

    private final PostRepository repository;

    public PostService(PostRepository repository) {
        this.repository = repository;
    }
}
Vermeiden
@Service
public class PostService {

    @Autowired
    private PostRepository repository;
    // hidden dependency, hard to test in isolation
}

Daten aus Controllern zurückgeben

Das Exponieren von JPA-Entities koppelt deine API an das Schema. Mappe diese auf ein DTO, damit Lazy-Felder, interne Spalten und zukünftige Migrationen privat bleiben.

Bevorzugt
@GetMapping("/{id}")
public PostResponse get(@PathVariable Long id) {
    return service.findById(id);
}
Vermeiden
@GetMapping("/{id}")
public Post get(@PathVariable Long id) {
    return repository.findById(id).orElseThrow();
    // serialising the entity leaks the schema
}

Abwägungen

Ist Spring Boot den Overhead wert?

Spring Boot belohnt Teams, die eine bewährte, integrierte Plattform suchen. Der Preis dafür sind die Startzeit, der Speicherverbrauch und eine große Lernfläche.

Strengths

  • Alles ist bereits integriert

    Web, Daten, Security, Validierung, Messaging und Observability teilen sich ein Konfigurationsmodell, sodass man selten Bibliotheken selbst zusammenfügen muss.

  • Das Ökosystem und der Talentpool sind enorm

    Es ist der Standard-Enterprise-Java-Stack. Bibliotheken zielen zuerst darauf ab, und es ist einfach, erfahrene Entwickler zu finden.

  • Produktionsfeatures sind erstklassig

    Actuator, externalisierte Konfiguration, Profile und Metrics behandeln den Betrieb als Design-Anliegen, nicht als nachträglichen Gedanken.

Trade-offs

  • Startzeit und Speicherkosten

    Ein Spring Boot Service startet langsamer und verbraucht mehr Speicher als ein minimales Framework. Bei Serverless oder vielen kleinen Services ist dieser Overhead spürbar.

  • Die Lernkurve ist steil

    Die Auto-Konfiguration verbirgt viel. Wenn der Standardwert falsch ist, muss man den zugrunde liegenden Spring-Context verstehen, um ihn zu debuggen.

  • Annotation-Magie kann den Flow verschleiern

    Das Verhalten verteilt sich über Annotationen, Aspekte und Proxies. Einem Request vom Anfang bis zum Ende zu folgen, erfordert Übung und gute Tooling.

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Spring Boot zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Spring Boot — mit Quizzen und echtem Code, den Sie im Browser ausführen können.