Spring Boot ist das populärste Java-Framework für Webanwendungen. Es erstellt ausführbare JAR-Dateien mit eingebettetem Tomcat-Server, die einfach zu deployen sind.

Java installieren

OpenJDK

# Debian/Ubuntu
apt install openjdk-21-jdk

# CentOS/RHEL
dnf install java-21-openjdk-devel

# Version prüfen
java -version

JAVA_HOME setzen

echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> /etc/profile.d/java.sh
echo 'export PATH=$PATH:$JAVA_HOME/bin' >> /etc/profile.d/java.sh
source /etc/profile.d/java.sh

Spring Boot Projekt

Build-Tools

# Maven
apt install maven

# Oder Gradle
apt install gradle

Projekt erstellen

# Spring Initializr
curl https://start.spring.io/starter.zip \
    -d type=maven-project \
    -d language=java \
    -d bootVersion=3.2.0 \
    -d baseDir=myapp \
    -d groupId=de.example \
    -d artifactId=myapp \
    -d name=myapp \
    -d dependencies=web,data-jpa,postgresql \
    -o myapp.zip

unzip myapp.zip
cd myapp

Einfache REST-API

// src/main/java/de/example/myapp/MyappApplication.java

package de.example.myapp;

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

@SpringBootApplication
public class MyappApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyappApplication.class, args);
    }
}
// src/main/java/de/example/myapp/controller/ApiController.java

package de.example.myapp.controller;

import org.springframework.web.bind.annotation.*;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/health")
    public Map<String, String> health() {
        return Map.of("status", "healthy");
    }

    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.findAll();
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
}

application.properties

# src/main/resources/application.properties

# Server
server.port=8080
server.servlet.context-path=/

# Datenbank
spring.datasource.url=jdbc:postgresql://localhost:5432/myapp
spring.datasource.username=${DB_USER:myapp}
spring.datasource.password=${DB_PASSWORD}
spring.jpa.hibernate.ddl-auto=validate

# Logging
logging.level.root=INFO
logging.level.de.example=DEBUG
logging.file.name=/var/log/myapp/application.log

# Actuator (Monitoring)
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always

application-production.properties

# src/main/resources/application-production.properties

spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:myapp}
spring.datasource.hikari.maximum-pool-size=20
spring.jpa.show-sql=false

# Compression
server.compression.enabled=true
server.compression.mime-types=application/json,text/html,text/plain

# SSL (optional, wenn nicht Nginx)
# server.ssl.key-store=classpath:keystore.p12
# server.ssl.key-store-password=${KEYSTORE_PASSWORD}

JAR erstellen

Mit Maven

./mvnw clean package -DskipTests
# Ergebnis: target/myapp-0.0.1-SNAPSHOT.jar

Mit Gradle

./gradlew bootJar
# Ergebnis: build/libs/myapp-0.0.1-SNAPSHOT.jar

Ausführbare JAR

# Test
java -jar target/myapp-0.0.1-SNAPSHOT.jar

# Mit Profil
java -jar -Dspring.profiles.active=production target/myapp-0.0.1-SNAPSHOT.jar

Deployment

Verzeichnisstruktur

mkdir -p /var/www/myapp
mkdir -p /var/log/myapp
mkdir -p /etc/myapp

# JAR kopieren
cp target/myapp-0.0.1-SNAPSHOT.jar /var/www/myapp/myapp.jar

# Berechtigungen
chown -R www-data:www-data /var/www/myapp
chown -R www-data:www-data /var/log/myapp

Konfigurationsdatei

# /etc/myapp/application.properties

spring.profiles.active=production
server.port=8080

# Datenbank
spring.datasource.url=jdbc:postgresql://localhost:5432/myapp
spring.datasource.username=myapp
spring.datasource.password=sicheres_passwort

# Logging
logging.file.name=/var/log/myapp/application.log

Systemd-Service

myapp.service

# /etc/systemd/system/myapp.service

[Unit]
Description=My Spring Boot Application
After=network.target postgresql.service

[Service]
Type=simple
User=www-data
Group=www-data

WorkingDirectory=/var/www/myapp

Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64"
Environment="SPRING_CONFIG_LOCATION=/etc/myapp/"

ExecStart=/usr/bin/java \
    -Xms256m \
    -Xmx512m \
    -XX:+UseG1GC \
    -jar /var/www/myapp/myapp.jar

ExecStop=/bin/kill -15 $MAINPID

Restart=on-failure
RestartSec=10

SuccessExitStatus=143
TimeoutStopSec=30

StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

JVM-Optionen

# Für größere Apps
ExecStart=/usr/bin/java \
    -Xms512m \
    -Xmx2g \
    -XX:+UseG1GC \
    -XX:MaxGCPauseMillis=200 \
    -XX:+HeapDumpOnOutOfMemoryError \
    -XX:HeapDumpPath=/var/log/myapp/heapdump.hprof \
    -Djava.security.egd=file:/dev/./urandom \
    -jar /var/www/myapp/myapp.jar

Service starten

systemctl daemon-reload
systemctl enable myapp
systemctl start myapp
systemctl status myapp

# Logs
journalctl -u myapp -f

Nginx Reverse Proxy

# /etc/nginx/sites-available/myapp

upstream springboot {
    server 127.0.0.1:8080;
    keepalive 32;
}

server {
    listen 80;
    server_name api.example.de;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.example.de;

    ssl_certificate /etc/letsencrypt/live/api.example.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.de/privkey.pem;

    access_log /var/log/nginx/myapp-access.log;
    error_log /var/log/nginx/myapp-error.log;

    client_max_body_size 10M;

    location / {
        proxy_pass http://springboot;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Actuator nur intern
    location /actuator {
        allow 127.0.0.1;
        deny all;
        proxy_pass http://springboot;
    }
}

Monitoring mit Actuator

Dependencies

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Endpoints

# Health Check
curl http://localhost:8080/actuator/health

# Metriken
curl http://localhost:8080/actuator/metrics
curl http://localhost:8080/actuator/metrics/jvm.memory.used

# Info
curl http://localhost:8080/actuator/info

Prometheus Integration

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
management.endpoints.web.exposure.include=prometheus,health
management.metrics.export.prometheus.enabled=true

Zero-Downtime Deployment

Blue-Green Deployment

#!/bin/bash
# deploy.sh

APP_PATH="/var/www/myapp"
NEW_JAR="myapp-new.jar"
CURRENT_JAR="myapp.jar"

# Neue Version kopieren
cp target/myapp-*.jar $APP_PATH/$NEW_JAR

# Health Check der neuen Version
java -jar $APP_PATH/$NEW_JAR --server.port=8081 &
NEW_PID=$!
sleep 30

if curl -f http://localhost:8081/actuator/health; then
    # Neue Version erfolgreich
    kill $NEW_PID

    # Austauschen
    mv $APP_PATH/$CURRENT_JAR $APP_PATH/myapp-old.jar
    mv $APP_PATH/$NEW_JAR $APP_PATH/$CURRENT_JAR

    systemctl restart myapp
    echo "Deployment erfolgreich"
else
    # Fehler
    kill $NEW_PID
    rm $APP_PATH/$NEW_JAR
    echo "Deployment fehlgeschlagen"
    exit 1
fi

Datenbank-Migration mit Flyway

Dependency

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

Migrations

-- src/main/resources/db/migration/V1__Create_users_table.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(100) NOT NULL,
    email VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
# application.properties
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration

Zusammenfassung

| Komponente | Funktion | |------------|----------| | Spring Boot | Framework | | Embedded Tomcat | Application Server | | Nginx | Reverse Proxy | | Actuator | Monitoring | | Flyway | DB-Migration |

| Befehl | Funktion | |--------|----------| | mvn package | JAR erstellen | | java -jar | Anwendung starten | | systemctl | Service-Management |

| JVM-Option | Funktion | |------------|----------| | -Xms | Minimaler Heap | | -Xmx | Maximaler Heap | | -XX:+UseG1GC | G1 Garbage Collector |

| Datei | Funktion | |-------|----------| | /var/www/myapp/myapp.jar | Anwendung | | /etc/myapp/application.properties | Konfiguration | | /var/log/myapp/ | Logs |

Fazit

Spring Boot vereinfacht das Java-Deployment erheblich. Ausführbare JARs mit eingebettetem Server sind einfach zu handhaben. Actuator bietet umfassendes Monitoring out-of-the-box. Mit Flyway lassen sich Datenbank-Migrationen automatisieren. Für Enterprise-Anwendungen bleibt Spring Boot die erste Wahl im Java-Ökosystem.