Spring Boot Aufbau-Kurs – Tag 1 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting


Custom Starter

🗺️ Deine Position im Kurs

TagThemaStatus
→ 1Auto-Configuration & Custom Starter👉 DU BIST HIER!
2Spring Data JPA Basics📜 Kommt als nächstes
3JPA Relationships & Queries🔒 Noch nicht freigeschaltet
4Spring Security – Part 1🔒 Noch nicht freigeschaltet
5Spring Security – Part 2🔒 Noch nicht freigeschaltet
6Caching & Serialisierung🔒 Noch nicht freigeschaltet
7Messaging & Email🔒 Noch nicht freigeschaltet
8Testing & Dokumentation🔒 Noch nicht freigeschaltet
9Spring Boot Actuator🔒 Noch nicht freigeschaltet
10Template Engines & Microservices🔒 Noch nicht freigeschaltet

Modul: Spring Boot Aufbau-Kurs (10 Arbeitstage)
Gesamt-Dauer: 10 Arbeitstage (je 8 Stunden)
Dein Ziel: Die Spring Boot „Magie“ verstehen & deinen eigenen Starter bauen


📋 Voraussetzungen für diesen Tag

Du brauchst:

  • ✅ Java SE Grundlagen (40 Tage) abgeschlossen
  • ✅ Java Web Basic + Aufbau (20 Tage) abgeschlossen
  • ✅ Spring Boot Basic (10 Tage) abgeschlossen
  • ✅ Du verstehst REST APIs, Dependency Injection und Maven

Tag verpasst oder später eingestiegen?
Kein Problem! Dieser Blogbeitrag deckt genau den Stoff von Tag 1 ab. Du kannst das komplette Projekt unten herunterladen und es in deinem eigenen Tempo durcharbeiten!


⚡ Kurze Zusammenfassung – Das Wichtigste in 30 Sekunden

Heute lüften wir das Geheimnis hinter Spring Boot’s „Magie“: Du baust einen eigenen Custom Starter (greeting-spring-boot-starter), der automatisch einen Service bereitstellt, der über application.properties konfigurierbar ist – genau wie spring-boot-starter-web oder andere offizielle Starter!

Du lernst heute:

  • @ConfigurationProperties für typsichere Konfiguration
  • @AutoConfiguration mit Conditional-Annotations
  • spring.factories für die Registrierung deines Starters
  • ✅ Die drei Säulen jedes Starters: Dependencies, Auto-Configuration, Properties

Am Ende des Tages: Du verstehst wie Spring Boot „denkt“ und kannst eigene wiederverwendbare Starter für deine Projekte bauen!


💻 Was du heute baust:

Heute bauen wir gemeinsam einen greeting-spring-boot-starter – einen vollwertigen Custom Starter, der automatisch einen GreetingService bereitstellt. Dabei lüften wir das Geheimnis, wie Spring Boot seine „Magie“ macht: Properties aus application.properties werden automatisch gebunden, Beans werden conditional erstellt, und alles funktioniert ohne dass du etwas konfigurieren musst!


🎯 Dein Lernpfad heute:

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

🟢 GRUNDLAGEN (Schritte 1-6)

Was du lernst:

  • Das Mysterium hinter Spring Boot verstehen
  • Projekt-Struktur für einen Custom Starter aufbauen
  • Service Interface und Implementierung erstellen
  • @ConfigurationProperties für typsichere Konfiguration nutzen
  • @AutoConfiguration und Conditionals verstehen
  • Den Starter via spring.factories registrieren

Ziel: Du hast einen funktionierenden Custom Starter, der automatisch einen Service bereitstellt und über Properties konfigurierbar ist.

Zeitaufwand: Ca. 4-5 Stunden

🟡 PROFESSIONALS (Schritte 7-8)

Was du lernst:

  • Den Starter in einem Test-Projekt einsetzen
  • Debug-Modus nutzen um Auto-Configuration zu analysieren
  • Conditions Evaluation Report verstehen

Ziel: Du weißt wie du Starter testest, debuggst und in Production einsetzt.

Zeitaufwand: Ca. 2 Stunden

🔵 BONUS: Advanced Features (Schritt 9)

Was du baust:

  • Eigene Implementierung überschreiben (Überschreibbarkeit)
  • Nested Properties für komplexe Konfigurationen
  • Advanced Conditional-Szenarien

Ziel: Du beherrschst fortgeschrittene Starter-Features für Enterprise-Anwendungen.

Zeitaufwand: Ca. 1-2 Stunden (optional)

💡 Tipp: Die Grundlagen (Schritte 1-6) sind essenziell – ohne sie verstehst du nicht, wie Spring Boot funktioniert. Professional (🟡) ist für alle wichtig, die produktionsreifen Code schreiben. Bonus (🔵) ist perfekt wenn du eigene komplexe Starter entwickeln möchtest oder einfach neugierig bist!


🟢 GRUNDLAGEN

Schritt 1: Das Mysterium verstehen

Hi! 👋

Elyndra hier – und heute tauchen wir gemeinsam in die Tiefen von Spring Boot ein.

Kennst du das Gefühl? Du fügst spring-boot-starter-web zu deinem Projekt hinzu und plötzlich:

  • Tomcat startet wie von Geisterhand
  • Jackson serialisiert JSON automatisch
  • Error Pages funktionieren einfach
  • Alles läuft – ohne eine Zeile Konfiguration!

„Das ist doch Magie!“ hörte ich letzte Woche von Nova. Und weißt du was? Sie hatte recht – es fühlt sich wie Magie an. Aber wie bei guter Magie steckt dahinter ein elegantes System, das wir heute entschlüsseln.

Die drei Säulen der Spring Boot „Magie“

Jeder Starter basiert auf drei Prinzipien:

1. Dependencies – Die benötigten Libraries werden mitgebracht
Wenn du spring-boot-starter-web hinzufügst, bekommst du automatisch Tomcat, Jackson, Spring MVC und alles was du brauchst. Du musst nicht jede Dependency einzeln hinzufügen!

2. Auto-Configuration – Beans werden automatisch erstellt
Spring Boot analysiert deinen Classpath und erstellt automatisch die richtigen Beans. Tomcat da? → Webserver wird konfiguriert. Jackson da? → JSON-Serialisierung läuft.

3. Properties – Konfiguration erfolgt deklarativ über application.properties
Statt Java-Code zu schreiben, schreibst du einfach server.port=8080 und Spring Boot versteht es.

Das ist wie ein gut organisiertes Werkzeugset: Alles was du brauchst ist dabei, nichts fehlt, nichts ist zu viel.

Unser Lernprojekt: greeting-spring-boot-starter

Heute bauen wir einen Starter, der so funktioniert:

// Du schreibst nur:
@Autowired
private GreetingService greetingService;

String greeting = greetingService.greet("Anna");
// Output: "Hello, Anna!"

Konfigurierbar über application.properties:

greeting.message=Guten Tag
greeting.uppercase=true
greeting.prefix=[VIP]

# Output wird: "[VIP] GUTEN TAG, ANNA!"

Das Schöne: Wir bauen das Schritt für Schritt nach und du verstehst dabei, wie Spring Boot „denkt“.


Schritt 2: Projekt-Struktur aufsetzen

Lass uns strukturiert vorgehen – wie beim Refactoring von Legacy-Code: Erst die Architektur verstehen, dann implementieren.

Maven Projekt erstellen

Unsere Projekt-Struktur wird so aussehen:

greeting-spring-boot-starter/
├── pom.xml
└── src/main/java/com/javafleet/greeting/
    ├── GreetingService.java
    ├── DefaultGreetingService.java
    ├── GreetingProperties.java
    └── GreetingAutoConfiguration.java
└── src/main/resources/META-INF/spring/
    └── org.springframework.boot.autoconfigure.AutoConfiguration.imports

Warum diese Struktur? Jede Klasse hat eine klare Verantwortung – Single Responsibility Principle in Aktion!

  • GreetingService.java – Das Interface (der Vertrag)
  • DefaultGreetingService.java – Die Standard-Implementierung
  • GreetingProperties.java – Konfiguration aus application.properties
  • GreetingAutoConfiguration.java – Die Auto-Configuration-Logik
  • AutoConfiguration.imports – Registrierung des Starters (Spring Boot 3!)

Die pom.xml (Spring Boot 3!)

Erstelle ein neues Maven-Projekt mit dieser pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.javafleet</groupId>
    <artifactId>greeting-spring-boot-starter</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>Greeting Spring Boot Starter</name>
    <description>Auto-configures a GreetingService</description>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot.version>3.2.0</spring-boot.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Auto-Configuration -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <!-- Configuration Properties Processor - Das ist wichtig! -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>${spring-boot.version}</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Wichtig für Spring Boot 3:

  • ✅ Minimum Java 17
  • ✅ Spring Boot 3.2.0 oder höher
  • spring-boot-configuration-processor für IDE Auto-Complete (gleich siehst du warum!)

💡 Tipp: Speichere die pom.xml und führe in deiner IDE „Maven → Reload Project“ aus, damit alle Dependencies heruntergeladen werden.


Schritt 3: Das Service Interface

Beginnen wir mit dem einfachsten Teil – dem Interface. Das ist wie das Fundament eines Hauses: solide und klar definiert.

GreetingService.java

Erstelle die Datei src/main/java/com/javafleet/greeting/GreetingService.java:

package com.javafleet.greeting;

/**
 * Service für Begrüßungen.
 * Wird automatisch von der Auto-Configuration bereitgestellt.
 */
public interface GreetingService {
    
    /**
     * Erstellt eine Begrüßung für den angegebenen Namen.
     * 
     * @param name Der zu begrüßende Name
     * @return Die formatierte Begrüßung
     */
    String greet(String name);
}

Was macht dieses Interface?

  • Es definiert den Vertrag für unseren Service
  • Jede Implementierung muss die Methode greet(String name) bereitstellen
  • Es ist bewusst einfach gehalten – später kannst du es erweitern!

💡 Zwischencheck: Hast du die Datei erstellt? Compiliert sie ohne Fehler? Gut! Weiter geht’s.


Schritt 4: Die Standard-Implementierung

Jetzt implementieren wir das Interface mit einer konkreten Klasse.

DefaultGreetingService.java

Erstelle die Datei src/main/java/com/javafleet/greeting/DefaultGreetingService.java:

package com.javafleet.greeting;

/**
 * Standard-Implementierung des GreetingService.
 * Diese Klasse wird automatisch von Spring Boot als Bean erstellt.
 */
public class DefaultGreetingService implements GreetingService {

    private final GreetingProperties properties;

    /**
     * Constructor Injection - Best Practice in Spring!
     * Die Properties werden automatisch injiziert.
     */
    public DefaultGreetingService(GreetingProperties properties) {
        this.properties = properties;
    }

    @Override
    public String greet(String name) {
        // Die Begrüßungs-Message aus den Properties holen
        String message = properties.getMessage();
        
        // Prefix hinzufügen wenn konfiguriert
        if (properties.getPrefix() != null && !properties.getPrefix().isEmpty()) {
            message = properties.getPrefix() + " " + message;
        }
        
        // Namen hinzufügen
        String greeting = message + ", " + name + "!";
        
        // Uppercase wenn konfiguriert
        if (properties.isUppercase()) {
            greeting = greeting.toUpperCase();
        }
        
        return greeting;
    }
}

Was passiert hier?

  1. Constructor Injection: Wir nutzen Constructor Injection (Best Practice!) um die GreetingProperties zu bekommen
  2. Properties nutzen: Die Konfiguration aus application.properties fließt über GreetingProperties in unseren Service
  3. Logik: Wir bauen die Begrüßung dynamisch zusammen – mit Prefix, Message, Name und optional Uppercase

Aber wo kommen die GreetingProperties her? Das machen wir jetzt!

💡 Zwischencheck: Die Klasse wird noch nicht kompilieren, weil GreetingProperties fehlt. Das ist normal! Weiter zum nächsten Schritt.


Schritt 5: Configuration Properties – Der Schlüssel zur Magie

Jetzt kommt der wichtigste Teil: Wie binden wir application.properties an Java-Klassen?

GreetingProperties.java

Erstelle die Datei src/main/java/com/javafleet/greeting/GreetingProperties.java:

package com.javafleet.greeting;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * Configuration Properties für den Greeting Starter.
 * Alle Properties mit dem Prefix "greeting" werden automatisch gebunden.
 * 
 * Beispiel in application.properties:
 * greeting.message=Hello
 * greeting.uppercase=true
 * greeting.prefix=[VIP]
 */
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {

    /**
     * Die Begrüßungs-Message. Standard: "Hello"
     */
    private String message = "Hello";

    /**
     * Soll die Ausgabe in Großbuchstaben sein? Standard: false
     */
    private boolean uppercase = false;

    /**
     * Optionaler Prefix vor der Message
     */
    private String prefix = "";

    // Getter und Setter

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public boolean isUppercase() {
        return uppercase;
    }

    public void setUppercase(boolean uppercase) {
        this.uppercase = uppercase;
    }

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
}

Das ist die Magie! 🎩✨

Die Annotation @ConfigurationProperties(prefix = "greeting") sagt Spring Boot:

  1. „Suche in application.properties nach Properties die mit greeting. beginnen“
  2. „Binde sie automatisch an die Felder dieser Klasse“
  3. „Nutze die Setter-Methoden zum Setzen der Werte“

Beispiel:

# In application.properties
greeting.message=Guten Tag
greeting.uppercase=true
greeting.prefix=[PREMIUM]

Spring Boot macht automatisch:

properties.setMessage("Guten Tag");      // greeting.message
properties.setUppercase(true);            // greeting.uppercase
properties.setPrefix("[PREMIUM]");        // greeting.prefix

Default-Werte: Wenn keine Properties gesetzt sind, greifen die Default-Werte ("Hello", false, "").

💡 Zwischencheck: Jetzt sollte dein Code kompilieren! Wenn nicht, prüfe ob alle drei Klassen (Interface, Service, Properties) erstellt sind.


Schritt 6: Auto-Configuration – Das Herzstück

Jetzt kommt der spannendste Teil: Die Auto-Configuration! Hier entscheiden wir, wann und wie unser Service automatisch erstellt wird.

GreetingAutoConfiguration.java

Erstelle die Datei src/main/java/com/javafleet/greeting/GreetingAutoConfiguration.java:

package com.javafleet.greeting;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

/**
 * Auto-Configuration für den Greeting Starter.
 * Diese Klasse wird automatisch von Spring Boot geladen.
 */
@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {

    /**
     * Erstellt den GreetingService Bean.
     * 
     * @ConditionalOnMissingBean: Nur erstellen wenn der User nicht bereits
     *                             eine eigene Implementierung bereitgestellt hat
     * @ConditionalOnProperty: Nur erstellen wenn nicht explizit disabled
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(
        prefix = "greeting",
        name = "enabled",
        havingValue = "true",
        matchIfMissing = true
    )
    public GreetingService greetingService(GreetingProperties properties) {
        return new DefaultGreetingService(properties);
    }
}

Was passiert hier? Lass uns Schritt für Schritt durchgehen:

@AutoConfiguration

Diese Annotation sagt Spring Boot: „Ich bin eine Auto-Configuration-Klasse, lade mich automatisch beim Start!“

Das ist neu in Spring Boot 3 und ersetzt das alte @Configuration + @EnableAutoConfiguration.

@ConditionalOnClass(GreetingService.class)

Bedeutung: „Lade mich nur wenn GreetingService im Classpath ist“

Warum? Stell dir vor, jemand fügt deinen Starter als Dependency hinzu, aber hat die Library nicht richtig geladen. Ohne diese Annotation würde Spring Boot beim Start crashen. Mit dieser Annotation wird die Configuration einfach übersprungen wenn die Klasse fehlt.

@EnableConfigurationProperties(GreetingProperties.class)

Bedeutung: „Aktiviere die Configuration Properties und erstelle automatisch einen Bean für GreetingProperties

Das sorgt dafür, dass Spring Boot:

  1. Die GreetingProperties Klasse findet
  2. Die Werte aus application.properties bindet
  3. Einen Bean erstellt den wir injizieren können

@Bean

Standard Spring Annotation: „Die Methode erstellt einen Bean der im Application Context verfügbar ist“

@ConditionalOnMissingBean

Das ist brilliant! 🎯

Bedeutung: „Erstelle diesen Bean nur wenn der User nicht bereits einen eigenen GreetingService Bean definiert hat“

Beispiel: Wenn ein User deinen Starter nutzt, aber seine eigene Implementierung haben will:

@Configuration
public class MyConfig {
    @Bean
    public GreetingService greetingService() {
        return new MyCustomGreetingService(); // Eigene Implementierung!
    }
}

Dann wird deine Default-Implementierung nicht geladen! Spring Boot merkt: „Oh, da gibt’s schon einen GreetingService Bean, ich muss keinen erstellen.“

Das nennt man Überschreibbarkeit – ein wichtiges Prinzip für gute Starter!

@ConditionalOnProperty

Bedeutung: „Erstelle diesen Bean nur unter bestimmten Property-Bedingungen“

Lass uns die Parameter durchgehen:

@ConditionalOnProperty(
    prefix = "greeting",           // Schaue bei "greeting.*" Properties
    name = "enabled",              // Konkret: "greeting.enabled"
    havingValue = "true",          // Muss "true" sein
    matchIfMissing = true          // WICHTIG: Was wenn Property fehlt?
)

Was heißt matchIfMissing = true?

  • Wenn greeting.enabled NICHT in application.properties steht: Bean wird erstellt ✅
  • Wenn greeting.enabled=true steht: Bean wird erstellt ✅
  • Wenn greeting.enabled=false steht: Bean wird NICHT erstellt ❌

Das folgt dem Spring Boot Prinzip: Convention over Configuration

Der Starter soll out-of-the-box funktionieren, ohne dass du irgendwas konfigurieren musst. Aber du kannst ihn jederzeit mit greeting.enabled=false ausschalten!

💡 Zwischencheck:

  • Hast du die Auto-Configuration erstellt?
  • Verstehst du die Conditional-Annotations?
  • Wenn ja → Weiter zum letzten Schritt der Grundlagen!

Schritt 7: Starter registrieren – spring.factories (Spring Boot 3!)

Fast geschafft! 🎉

Jetzt müssen wir Spring Boot nur noch sagen: „Hey, schau dir diese Auto-Configuration an!“

In Spring Boot 3 hat sich das Registrierungssystem geändert. Wir nutzen jetzt die Datei AutoConfiguration.imports statt spring.factories.

AutoConfiguration.imports erstellen

Erstelle die Datei:
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Wichtig: Der komplette Pfad muss exakt so sein! META-INF/spring/ ist das neue Verzeichnis in Spring Boot 3.

Inhalt der Datei:

com.javafleet.greeting.GreetingAutoConfiguration

Das war’s! Diese eine Zeile sagt Spring Boot:
„Lade diese Auto-Configuration beim Start!“

Was passiert beim Start?

Wenn eine Spring Boot Anwendung startet:

  1. Spring Boot scannt alle JARs im Classpath
  2. Es sucht nach META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  3. Es lädt alle dort aufgelisteten Klassen
  4. Es prüft die @Conditional... Annotations
  5. Wenn alle Bedingungen erfüllt sind → Beans werden erstellt!

💡 Zwischencheck:

greeting-spring-boot-starter/
├── pom.xml
├── src/main/java/com/javafleet/greeting/
│   ├── GreetingService.java
│   ├── DefaultGreetingService.java
│   ├── GreetingProperties.java
│   └── GreetingAutoConfiguration.java
└── src/main/resources/META-INF/spring/
    └── org.springframework.boot.autoconfigure.AutoConfiguration.imports

Hast du alle Dateien? Dann lass uns den Starter bauen!

Starter bauen & installieren

Öffne ein Terminal im Projekt-Verzeichnis und führe aus:

mvn clean install

Was passiert:

  • Maven kompiliert deinen Code
  • Erstellt ein JAR: target/greeting-spring-boot-starter-1.0.0.jar
  • Installiert es in dein lokales Maven Repository (~/.m2/repository/)

Gratulation! 🎉 Du hast soeben deinen ersten Custom Spring Boot Starter gebaut!

Aber funktioniert er auch? Das testen wir jetzt!


🟡 PROFESSIONALS

Schritt 8: Starter testen – Das Demo-Projekt

Jetzt wird’s spannend! Wir erstellen ein Test-Projekt um unseren Starter auszuprobieren.

Demo-Projekt erstellen

Erstelle ein neues Spring Boot Projekt (mit Spring Initializr oder manuell):

Struktur:

greeting-demo/
├── pom.xml
└── src/main/java/com/javafleet/demo/
    └── DemoApplication.java
└── src/main/resources/
    └── application.properties

pom.xml des Demo-Projekts

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
    </parent>

    <groupId>com.javafleet</groupId>
    <artifactId>greeting-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- Unser Custom Starter! -->
        <dependency>
            <groupId>com.javafleet</groupId>
            <artifactId>greeting-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!-- Spring Boot Starter für Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Wichtig: Wir fügen unseren Starter als Dependency hinzu! greeting-spring-boot-starter mit Version 1.0.0.

DemoApplication.java

package com.javafleet.demo;

import com.javafleet.greeting.GreetingService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class DemoApplication {

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

    /**
     * CommandLineRunner wird automatisch beim Start ausgeführt.
     * Perfekt zum Testen!
     */
    @Bean
    public CommandLineRunner demo(GreetingService greetingService) {
        return args -> {
            // Test 1: Einfache Begrüßung
            System.out.println(greetingService.greet("Franz"));
            
            // Test 2: Mit anderem Namen
            System.out.println(greetingService.greet("Nova"));
            
            // Test 3: Mit Elyndra
            System.out.println(greetingService.greet("Elyndra"));
        };
    }
}

Was passiert hier?

  1. @SpringBootApplication startet Spring Boot
  2. CommandLineRunner ist ein Interface das beim Start automatisch ausgeführt wird
  3. Wir injizieren GreetingServiceohne ihn selbst zu erstellen!
  4. Spring Boot hat durch unseren Starter automatisch einen GreetingService Bean erstellt!

application.properties (leer lassen!)

# Erstmal keine Configuration - nutzen wir die Defaults

Starter testen – Test 1: Defaults

cd greeting-demo
mvn spring-boot:run

Erwartete Ausgabe:

Hello, Franz!
Hello, Nova!
Hello, Elyndra!

Funktioniert’s? 🎉 Gratulation! Dein Starter arbeitet mit Default-Werten!

Funktioniert’s nicht?

  • Prüfe ob mvn clean install im Starter-Projekt erfolgreich war
  • Prüfe die Konsole auf Fehlermeldungen
  • Stelle sicher dass die Dependency korrekt in der pom.xml steht

Test 2: Properties konfigurieren

Jetzt probieren wir die Konfiguration aus!

Bearbeite application.properties:

greeting.message=Guten Tag
greeting.uppercase=false
greeting.prefix=

Starte neu:

mvn spring-boot:run

Erwartete Ausgabe:

Guten Tag, Franz!
Guten Tag, Nova!
Guten Tag, Elyndra!

Siehst du? Die Message hat sich geändert! 🎯


Test 3: Uppercase & Prefix

Bearbeite application.properties:

greeting.message=Willkommen
greeting.uppercase=true
greeting.prefix=[VIP]

Starte neu:

mvn spring-boot:run

Erwartete Ausgabe:

[VIP] WILLKOMMEN, FRANZ!
[VIP] WILLKOMMEN, NOVA!
[VIP] WILLKOMMEN, ELYNDRA!

Perfekt! 🚀 Alle drei Properties funktionieren!


Test 4: Starter deaktivieren

Bearbeite application.properties:

greeting.enabled=false

Starte neu:

mvn spring-boot:run

Erwartetes Ergebnis: Die Anwendung startet nicht!

Fehler:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method demo in com.javafleet.demo.DemoApplication required a bean of type 
'com.javafleet.greeting.GreetingService' that could not be found.

Warum?

  • greeting.enabled=false → Auto-Configuration lädt nicht → Kein GreetingService Bean
  • DemoApplication versucht GreetingService zu injizieren → Bean fehlt → Fehler!

Das ist korrekt! So funktioniert @ConditionalOnProperty – du kannst Features an/ausschalten!

💡 Tipp: Lösche die Zeile greeting.enabled=false wieder, damit die Tests weiterlaufen.


Schritt 9: Debug-Modus – Verstehe was Spring Boot macht

Jetzt kommt ein mächtiges Tool: Der Debug-Modus!

Debug aktivieren

Füge in application.properties hinzu:

debug=true

greeting.message=Hello
greeting.uppercase=false
greeting.prefix=

Starte die Anwendung:

mvn spring-boot:run

Conditions Evaluation Report

Spring Boot gibt dir jetzt einen detaillierten Report in der Konsole:

============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------

   GreetingAutoConfiguration matched:
      - @ConditionalOnClass found required class 'com.javafleet.greeting.GreetingService' (OnClassCondition)

   GreetingAutoConfiguration#greetingService matched:
      - @ConditionalOnMissingBean (types: com.javafleet.greeting.GreetingService; SearchStrategy: all) did not find any beans (OnBeanCondition)
      - @ConditionalOnProperty (greeting.enabled=true) matched (OnPropertyCondition)

Negative matches:
-----------------

   DataSourceAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.sql.DataSource' (OnClassCondition)

Was sagt uns das?

Positive matches = Diese Auto-Configurations wurden geladen:

  • GreetingAutoConfiguration matched – alle Bedingungen erfüllt!
  • @ConditionalOnClassGreetingService gefunden
  • @ConditionalOnMissingBean – Kein anderer GreetingService Bean existiert
  • @ConditionalOnPropertygreeting.enabled ist true (oder fehlt)

Negative matches = Diese wurden NICHT geladen:

  • DataSourceAutoConfigurationDataSource Klasse fehlt im Classpath

💡 Pro-Tipp: Nutze debug=true immer wenn:

  • Deine Auto-Configuration nicht lädt
  • Du verstehen willst warum ein Bean nicht erstellt wurde
  • Du debuggen musst welche Conditionals nicht matchen

IDE Auto-Complete testen

Wenn du IntelliJ IDEA oder Eclipse nutzt, solltest du jetzt Auto-Complete für deine Properties haben!

Probier’s aus:

  1. Öffne application.properties
  2. Tippe greeting. und warte
  3. Die IDE sollte dir vorschlagen:
    • greeting.message
    • greeting.uppercase
    • greeting.prefix
    • greeting.enabled

Warum funktioniert das?

Erinnerst du dich an spring-boot-configuration-processor in der pom.xml?

Dieser Processor:

  1. Scannt alle @ConfigurationProperties Klassen
  2. Generiert eine Datei: META-INF/spring-configuration-metadata.json
  3. IDEs lesen diese Datei und bieten Auto-Complete an!

Schaue dir die Datei an:
target/classes/META-INF/spring-configuration-metadata.json

{
  "properties": [
    {
      "name": "greeting.message",
      "type": "java.lang.String",
      "description": "Die Begrüßungs-Message.",
      "defaultValue": "Hello"
    },
    {
      "name": "greeting.uppercase",
      "type": "java.lang.Boolean",
      "description": "Soll die Ausgabe in Großbuchstaben sein?",
      "defaultValue": false
    },
    {
      "name": "greeting.prefix",
      "type": "java.lang.String",
      "description": "Optionaler Prefix vor der Message.",
      "defaultValue": ""
    }
  ]
}

Das ist Production-Ready! Professionelle Starter haben immer diese Metadata!


🔵 BONUS: Advanced Features

Gratulation! 🎉 Du hast die Grundlagen und Professional-Level gemeistert!

Jetzt zeige ich dir fortgeschrittene Features für Enterprise-Starter.

Bonus 1: Eigene Implementierung überschreiben

Erinnere dich an @ConditionalOnMissingBean? Lass uns das testen!

Eigene Implementierung erstellen

In greeting-demo erstellen:
src/main/java/com/javafleet/demo/CustomGreetingService.java

package com.javafleet.demo;

import com.javafleet.greeting.GreetingService;
import org.springframework.stereotype.Service;

/**
 * Eigene Implementierung - überschreibt den Default!
 */
@Service
public class CustomGreetingService implements GreetingService {

    @Override
    public String greet(String name) {
        return "🎉 Custom Greeting: Hey " + name + ", you're awesome! 🎉";
    }
}

Starte die Anwendung:

mvn spring-boot:run

Ausgabe:

🎉 Custom Greeting: Hey Franz, you're awesome! 🎉
🎉 Custom Greeting: Hey Nova, you're awesome! 🎉
🎉 Custom Greeting: Hey Elyndra, you're awesome! 🎉

Was ist passiert?

  1. Spring Boot findet CustomGreetingService (wegen @Service)
  2. Es prüft @ConditionalOnMissingBean in GreetingAutoConfiguration
  3. Bedingung nicht erfüllt! Es gibt bereits einen GreetingService Bean!
  4. DefaultGreetingService wird nicht erstellt
  5. CustomGreetingService wird genutzt ✅

Das nennt man Überschreibbarkeit! Ein wichtiges Prinzip für flexible Starter.

💡 Pro-Tipp: Lösche die CustomGreetingService Klasse wieder für die nächsten Tests.


Bonus 2: Nested Properties – Komplexe Konfiguration

Für komplexere Konfigurationen kannst du Properties verschachteln!

GreetingProperties erweitern

Bearbeite GreetingProperties.java im Starter:

package com.javafleet.greeting;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {

    private String message = "Hello";
    private boolean uppercase = false;
    private String prefix = "";

    /**
     * Nested Properties für Format-Optionen
     */
    private Format format = new Format();

    // Getter/Setter für format
    public Format getFormat() {
        return format;
    }

    public void setFormat(Format format) {
        this.format = format;
    }

    // Bestehende Getter/Setter...
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public boolean isUppercase() {
        return uppercase;
    }

    public void setUppercase(boolean uppercase) {
        this.uppercase = uppercase;
    }

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    /**
     * Nested Class für Format-Konfiguration
     */
    public static class Format {
        private String emoji = "";
        private String separator = ", ";

        public String getEmoji() {
            return emoji;
        }

        public void setEmoji(String emoji) {
            this.emoji = emoji;
        }

        public String getSeparator() {
            return separator;
        }

        public void setSeparator(String separator) {
            this.separator = separator;
        }
    }
}

DefaultGreetingService anpassen

Bearbeite DefaultGreetingService.java:

@Override
public String greet(String name) {
    String message = properties.getMessage();
    
    // Prefix hinzufügen
    if (properties.getPrefix() != null && !properties.getPrefix().isEmpty()) {
        message = properties.getPrefix() + " " + message;
    }
    
    // Separator aus Format nutzen
    String separator = properties.getFormat().getSeparator();
    String greeting = message + separator + name + "!";
    
    // Uppercase
    if (properties.isUppercase()) {
        greeting = greeting.toUpperCase();
    }
    
    // Emoji hinzufügen
    String emoji = properties.getFormat().getEmoji();
    if (emoji != null && !emoji.isEmpty()) {
        greeting = emoji + " " + greeting + " " + emoji;
    }
    
    return greeting;
}

Starter neu bauen & testen

# Im Starter-Projekt
mvn clean install

# Im Demo-Projekt
cd ../greeting-demo

Bearbeite application.properties:

greeting.message=Hello
greeting.uppercase=false
greeting.prefix=
greeting.format.emoji=👋
greeting.format.separator= -> 

Starte:

mvn spring-boot:run

Ausgabe:

👋 Hello -> Franz! 👋
👋 Hello -> Nova! 👋
👋 Hello -> Elyndra! 👋

Brilliant! 🎯 Beliebig verschachtelte Properties!

IDE Auto-Complete zeigt jetzt auch:

greeting.
  ├── message
  ├── uppercase
  ├── prefix
  ├── enabled
  └── format.
      ├── emoji
      └── separator

Bonus 3: Advanced Conditionals

Du kannst mehrere Conditionals kombinieren für komplexe Szenarien!

Beispiel: Conditional on Multiple Classes

@AutoConfiguration
@ConditionalOnClass({GreetingService.class, SomeOtherClass.class})
public class GreetingAutoConfiguration {
    // Lädt nur wenn BEIDE Klassen im Classpath sind
}

Beispiel: Conditional on Bean

@Bean
@ConditionalOnBean(DataSource.class)
public GreetingService databaseGreetingService(DataSource dataSource) {
    // Lädt nur wenn ein DataSource Bean existiert
    return new DatabaseGreetingService(dataSource);
}

Beispiel: Custom Condition

Du kannst sogar eigene Conditions schreiben:

public class OnLinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return System.getProperty("os.name").toLowerCase().contains("linux");
    }
}

@Bean
@Conditional(OnLinuxCondition.class)
public GreetingService linuxGreetingService() {
    return new LinuxGreetingService();
}

💡 Pro-Tipp: Für Production-Starter überlege dir gut welche Conditionals du brauchst. Halte es so einfach wie möglich!


✅ Checkpoint: Hast du Tag 1 geschafft?

Grundlagen (🟢):

  • [ ] Du verstehst was @SpringBootApplication macht
  • [ ] Du kannst @ConfigurationProperties nutzen
  • [ ] Du kennst @ConditionalOnProperty, @ConditionalOnClass, @ConditionalOnMissingBean
  • [ ] Du hast AutoConfiguration.imports erstellt und verstanden
  • [ ] Du hast einen funktionierenden Starter gebaut

Professionals (🟡):

  • [ ] Deine IDE zeigt Auto-Complete für Properties
  • [ ] Du kannst den Starter mit enabled=false ausschalten
  • [ ] Du kannst Debug-Modus nutzen (debug=true)
  • [ ] Du verstehst den Conditions Evaluation Report

Bonus (🔵):

  • [ ] Du kannst den Default-Service überschreiben
  • [ ] Du hast Nested Properties implementiert
  • [ ] Du verstehst die Überschreibbarkeit durch @ConditionalOnMissingBean

Alles ✅? Du bist bereit für Tag 2!

Nicht alles funktioniert?

  • Schau nochmal in Schritt 8 (Testing)
  • Lade das komplette Projekt unten herunter
  • Prüfe ob AutoConfiguration.imports korrekt ist
  • Nutze debug=true für Troubleshooting
  • Stelle sicher dass mvn clean install erfolgreich war

🔥 Elyndras Real Talk:

Nach der Session kam das Team zusammen…

Nova: „Elyndra, das war… WOW! Ich dachte immer Spring Boot macht einfach Magie. Aber jetzt verstehe ich es! Es ist wie… ein super intelligentes System das entscheidet was geladen wird!“

Elyndra: „Genau, Nova! Und das Schöne ist: Du kannst jetzt eigene ‚Magie‘ bauen. Das ist wie beim Refactoring – wenn du verstehst wie etwas funktioniert, kannst du es verbessern oder erweitern.“

Franz-Martin: „Elyndra hat es perfekt erklärt. @ConditionalOnProperty ist übrigens mein Favorit – elegant und mächtig. Wir nutzen das in fast jedem unserer Enterprise-Starter.“

Nova: „Aber eine Frage: Warum matchIfMissing = true? Warum nicht einfach nur laden wenn es explizit aktiviert ist?“

Elyndra: „Gute Frage! Das folgt dem Spring Boot Prinzip: Convention over Configuration. Der Starter soll out-of-the-box funktionieren, ohne dass du etwas konfigurieren musst. Du kannst ihn aber jederzeit mit enabled=false ausschalten.“

Code Sentinel: „Wichtig aus Security-Perspektive: Überlegt gut was eure Starter standardmäßig tun. matchIfMissing = true bedeutet der Code läuft automatisch. Das kann bei Security-relevanten Features problematisch sein.“

Franz-Martin: „Absolut richtig, Code Sentinel. Bei unserem Audit-Logging-Starter haben wir deshalb matchIfMissing = false – Logging muss explizit aktiviert werden.“

Nova: „Oh! Also bei Security-Features lieber false als Default?“

Code Sentinel: „Genau. Principle of Least Privilege – nichts ist automatisch aktiv, was Sicherheitsrisiken bergen könnte.“

Elyndra: „Sehr guter Punkt! Das ist wie beim Legacy-Code: Verstehe erst die Implikationen, bevor du Defaults setzt. Bei unserem greeting-starter ist es unkritisch, aber bei Production-Startern müssen wir vorsichtig sein.“

Nova: „Ich liebe das! Jetzt kann ich eigene Starter bauen! Vielleicht einen todo-reminder-starter?“

Elyndra: lächelt „Das wäre ein perfektes Übungsprojekt! Fang klein an, genau wie wir heute.“


❓ FAQ (Häufige Fragen)

Q: Warum sehe ich kein Auto-Complete für meine Properties?
A: Stelle sicher dass:

  1. spring-boot-configuration-processor in der pom.xml ist
  2. Du Maven neu geladen hast (Reimport in IDE)
  3. Du die App mindestens einmal gebaut hast (mvn clean install)
  4. Die spring-configuration-metadata.json generiert wurde (in target/classes/META-INF/)

Q: Meine Auto-Configuration lädt nicht – wie debugge ich das?
A: Setze debug=true in application.properties und schau in die Console. Der „Conditions Evaluation Report“ zeigt dir GENAU welche Conditionals nicht erfüllt sind.

Q: Kann ich mehrere Auto-Configurations in einem Starter haben?
A: Ja! Einfach mehrere Zeilen in AutoConfiguration.imports einfügen:

com.javafleet.greeting.GreetingAutoConfiguration
com.javafleet.greeting.OtherAutoConfiguration

Q: Was ist der Unterschied zwischen @Configuration und @AutoConfiguration?
A: @AutoConfiguration ist neu in Spring Boot 3 und speziell für Auto-Configuration gedacht. Es hat bessere Ordering-Unterstützung und ist semantisch klarer.

Q: Wie kann ich die Reihenfolge von Auto-Configurations steuern?
A: Nutze @AutoConfigureBefore und @AutoConfigureAfter:

@AutoConfiguration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@AutoConfigureBefore(JpaAutoConfiguration.class)
public class GreetingAutoConfiguration {
    // ...
}

Q: Muss ich meinen Starter auf Maven Central publishen?
A: Nein! Für internen Gebrauch reicht mvn install um ihn lokal verfügbar zu machen. Für Teams kannst du einen Nexus/Artifactory verwenden.

Q: Was passiert mit dem Starter im Production Build?
A: Der Starter wird wie jede andere Dependency in dein JAR/WAR gepackt. Die Auto-Configuration läuft beim Start und erstellt die Beans. Keine Extra-Schritte nötig!

Q: Was macht ihr eigentlich wenn im Team mal Spannungen entstehen? Wie geht ihr mit zwischenmenschlichen Problemen um? 🤔
A: Gute Frage… Manche Geschichten passen nicht in Tech-Blogs. Die gehören eher zu Herz Schmerz und private logs. Aber das ist ein anderes Kapitel. 📖


📅 Nächster Kurstag: Tag 2

Morgen im Kurs / Nächster Blogbeitrag:

„Tag 2: Spring Data JPA Basics – Von ArrayList zur Datenbank“

Was du lernen wirst:

  • JPA Setup mit MariaDB
  • Erste Entity mit @Entity, @Id, @GeneratedValue
  • Repository Pattern mit JpaRepository
  • Service-Layer Umstellung von ArrayList zu Database
  • Transaction Management verstehen

Warum wichtig? Fast jede Enterprise-Anwendung braucht Datenpersistenz. Spring Data JPA macht aus mühsamer JDBC-Arbeit elegante Repository-Calls.

Voraussetzung: Tag 1 abgeschlossen

👉 Zum Blogbeitrag Tag 2 (erscheint morgen)


📚 Deine Fortschritts-Übersicht

TagThemaStatus
✅ 1Auto-Configuration & Custom StarterABGESCHLOSSEN! 🎉
→ 2Spring Data JPA BasicsAls nächstes
3JPA Relationships & QueriesNoch offen
4Spring Security – Part 1Noch offen
5Spring Security – Part 2Noch offen
6Caching & SerialisierungNoch offen
7Messaging & EmailNoch offen
8Testing & DokumentationNoch offen
9Spring Boot ActuatorNoch offen
10Template Engines & MicroservicesNoch offen

Du hast 10% des Kurses geschafft! 💪

Alle Blogbeiträge dieser Serie:
👉 Spring Boot Aufbau-Kurs – Komplette Übersicht


📥 Download & Ressourcen

Projekt zum Download:
👉 Github: greeting-spring-boot-starter

Was ist im Repository enthalten:

  • ✅ Kompletter Starter Code (Spring Boot 3)
  • ✅ Demo-Projekt zum Testen
  • application.properties Beispiele
  • ✅ Ausführliche README mit Schnellstart
  • ✅ Alle Bonus-Features

Projekt starten:

# Repository klonen
git clone https://github.com/javafleet/Tag1-Spring-Boot-Aufbau-Custom-Starter.git
cd Tag1-Spring-Boot-Aufbau-Custom-Starter

# 1. Starter bauen & installieren
cd greeting-spring-boot-starter
mvn clean install

# 2. Demo-App starten
cd ../greeting-demo
mvn spring-boot:run

# 3. Im Browser testen
# http://localhost:8080 sollte jetzt laufen

Probleme? Erstelle ein Issue auf Github oder kontaktiere uns!


Das war Tag 1 vom Spring Boot Aufbau-Kurs!

Du kannst jetzt:

  • ✅ Spring Boot Auto-Configuration verstehen
  • ✅ Eigene Starter mit @ConfigurationProperties entwickeln
  • @Conditional Annotations richtig einsetzen
  • AutoConfiguration.imports nutzen (Spring Boot 3!)
  • ✅ Die Spring Boot „Magie“ erklären
  • ✅ Properties mit IDE Auto-Complete konfigurieren

Morgen tauchen wir in Spring Data JPA ein – von ArrayList zur echten Datenbank! 🚀

Keep coding, keep learning! 💙


Tag 2 erscheint morgen im Kurs. Bis dahin: Happy Coding!


Tags: #SpringBoot #CustomStarter #AutoConfiguration #ConfigurationProperties #Java #Tutorial #Tag1

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.