9 Spring-Konfiguration

9.1 Java-basierte Konfiguration

Die Java-basierte Konfiguration in Spring ist eine moderne Alternative zur traditionellen XML-Konfiguration. Sie ermöglicht es, Spring-Beans und -Abhängigkeiten direkt in Java-Code zu definieren, wodurch die Konfiguration typsicher und refactor-freundlich wird.

9.1.1 Vorteile der Java-Konfiguration

9.1.2 Grundlegende Komponenten

9.1.2.1 @Configuration

Die @Configuration-Annotation kennzeichnet eine Klasse als Quelle für Bean-Definitionen.

@Configuration
public class AppConfig {
}

9.1.2.2 @Bean

Innerhalb einer mit @Configuration annotierten Klasse werden Methoden mit der @Bean-Annotation versehen, um anzugeben, dass eine Instanz der Rückgabetyp-Klasse von Spring verwaltet werden soll.

@Bean
public MyBean myBean() {
    return new MyBean();
}

9.1.2.3 Abhängigkeiten injizieren

Abhängigkeiten zwischen Beans werden durch einfaches Aufrufen anderer @Bean-Methoden in der Konfigurationsklasse injiziert.

@Bean
public MyBean myBean() {
    return new MyBean();
}

@Bean
public MyService myService() {
    return new MyService(myBean());
}

9.1.3 Beispiel einer Java-Konfiguration

Hier ist ein einfaches Beispiel, das die Definition einer Konfigurationsklasse und die Erstellung von zwei Beans zeigt:

@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setUrl("jdbc:h2:mem:testdb");
        dataSource.setUsername("sa");
        dataSource.setPassword("");
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

In diesem Beispiel wird eine DataSource-Bean und eine JdbcTemplate-Bean definiert. Die JdbcTemplate-Bean hängt von der DataSource-Bean ab, welche durch die Parameterinjektion der jdbcTemplate-Methode bereitgestellt wird.

9.1.4 Diagramm der Bean-Erstellung und -Injektion

Dieses Diagramm verdeutlicht die Erstellung und Abhängigkeitsinjektion der Beans in der Java-Konfiguration.

Die Java-basierte Konfiguration in Spring bietet eine leistungsstarke und flexible Alternative zur XML-Konfiguration. Mit ihr lassen sich Spring-Anwendungen auf eine Weise konfigurieren, die sowohl typsicher als auch übersichtlich ist. Durch die Nutzung von Annotationen wie @Configuration und @Bean können Entwickler komplexe Anwendungskontexte auf eine kohärente und wartungsfreundliche Weise definieren.

9.2 @Configuration und @Bean Annotations: Definition und Verwendung

In Spring ermöglichen die @Configuration und @Bean Annotations eine elegante und typsichere Art der Konfiguration. Diese Annotations sind zentral für die Java-basierte Konfiguration, die als Alternative zur traditionellen XML-Konfiguration dient.

9.2.1 Verwendung der @Configuration Annotation

Die @Configuration-Annotation kennzeichnet eine Klasse als Quelle für Bean-Definitionen. Klassen, die mit dieser Annotation versehen sind, werden von Spring zur Laufzeit verarbeitet, um Bean-Definitionen und Service-Anfragen zu erstellen.

@Configuration
public class AppConfig {
    // Bean-Definitionen
}

Eine mit @Configuration annotierte Klasse kann beliebig viele @Bean-annotierte Methoden enthalten und ermöglicht es, komplexe Konfigurationsstrukturen aufzubauen.

9.2.2 Definition und Verwendung von @Bean

Die @Bean-Annotation wird verwendet, um eine Methode zu kennzeichnen, deren Rückgabewert eine Bean definiert, die vom Spring IoC-Container verwaltet wird. Die Methode kann beliebige Java-Logik enthalten, was eine flexible Bean-Konfiguration ermöglicht.

@Bean
public MyService myService() {
    return new MyService();
}

Beans, die durch @Bean-Methoden in einer @Configuration-Klasse erstellt wurden, können in andere Beans injiziert werden, indem die entsprechenden Methoden innerhalb derselben Konfigurationsklasse aufgerufen oder die Beans über Konstruktor- bzw. Setter-Injektion in andere Komponenten injiziert werden.

9.2.3 Injektion von Abhängigkeiten zwischen Beans

Die Java-Konfiguration unterstützt sowohl die direkte Injektion von Bean-Abhängigkeiten über Methodenaufrufe innerhalb der Konfigurationsklasse als auch die traditionelle Autowired-Injektion.

@Bean
public MyRepository myRepository() {
    return new MyRepositoryImpl();
}

@Bean
public MyService myService(MyRepository myRepository) {
    return new MyService(myRepository);
}

In diesem Beispiel wird myRepository() innerhalb der myService(MyRepository myRepository) Methode aufgerufen, was eine explizite Bean-Abhängigkeit definiert. Alternativ kann Spring automatisch eine Bean vom Typ MyRepository injizieren, wenn sie im Kontext verfügbar ist.

9.2.4 Diagramm: Zusammenhang zwischen @Configuration und @Bean

Dieses Diagramm zeigt, wie eine @Configuration-Klasse (AppConfig) verwendet wird, um Beans (MyService und MyRepository) zu definieren und wie Abhängigkeiten zwischen diesen Beans verwaltet werden.

9.3 Teil 2: Fortgeschrittene Annotationen

9.3.1 Komponentenscan: Automatische Erkennung von Spring Beans mit @ComponentScan

Der Komponentenscan ist ein zentraler Mechanismus innerhalb des Spring Frameworks, um Spring Beans automatisch zu erkennen und zu registrieren. Durch die Annotation @ComponentScan wird Spring angewiesen, ein bestimmtes Paket und seine Unterpakete nach Klassen zu durchsuchen, die mit Stereotyp-Annotations (@Component, @Service, @Repository, @Controller etc.) versehen sind, und diese als Beans im Spring-Anwendungskontext zu registrieren.

9.3.1.1 Grundlagen des Komponentenscans

Die @ComponentScan-Annotation kann direkt in einer @Configuration-Klasse verwendet werden, um anzugeben, in welchen Paketen Spring nach Beans suchen soll. Falls keine spezifischen Pakete angegeben werden, durchsucht Spring das Paket, in dem die @Configuration-Klasse liegt, sowie alle Unterpakete.

@Configuration
@ComponentScan(basePackages = "com.example.myapp")
public class AppConfig {
}

In diesem Beispiel wird Spring angewiesen, im Paket com.example.myapp und seinen Unterpaketen nach mit @Component, @Service, @Repository, @Controller annotierten Klassen zu suchen.

9.3.1.2 Verwendung von Stereotyp-Annotations

Stereotyp-Annotations wie @Component, @Service, @Repository, und @Controller kennzeichnen Klassen für unterschiedliche Zwecke innerhalb der Anwendung und signalisieren Spring, dass diese Klassen automatisch als Beans instanziiert und in den Anwendungskontext aufgenommen werden sollen.

9.3.1.3 Beispiel einer automatisch erkannten Bean

@Service
public class MyService {
    // Service-Logik
}

Mit der Konfiguration von @ComponentScan in AppConfig und der @Service-Annotation in MyService erkennt und registriert Spring MyService automatisch als Bean.

9.3.1.4 Diagramm: Prozess des Komponentenscans

scans.svg

Dieses Diagramm veranschaulicht, wie @ComponentScan in einer @Configuration-Klasse definiert wird, um nach Beans zu suchen, die mit Stereotyp-Annotations versehen sind.

Der Komponentenscan und die Verwendung von Stereotyp-Annotations vereinfachen die Bean-Registrierung erheblich, indem sie eine deklarative Entdeckung und Registrierung von Komponenten ermöglichen. Diese Technik fördert eine saubere Trennung der Verantwortlichkeiten innerhalb der Anwendung und unterstützt eine effiziente Entwicklung, indem Boilerplate-Code für die explizite Bean-Registrierung vermieden wird.

9.3.2 Spezifische Stereotyp-Annotations: @Component, @Service, @Repository, und @Controller

Im Spring Framework dienen Stereotyp-Annotations dazu, die Rolle und das Verhalten von Klassen innerhalb der Anwendung zu definieren. Die vier Haupt-Stereotyp-Annotations – @Component, @Service, @Repository, und @Controller – ermöglichen eine semantische Markierung von Komponenten und fördern so eine klare Trennung der Verantwortlichkeiten innerhalb der Anwendung.

9.3.2.1 @Component

Die @Component-Annotation ist die allgemeinste Stereotyp-Annotation und kennzeichnet eine Klasse als Spring-Managed Component. Sie kann für jede Klasse verwendet werden, die vom Spring Container instanziiert und verwaltet werden soll.

@Component
public class MyComponent {
    // Klasse-Logik
}

9.3.2.2 @Service

Die @Service-Annotation wird speziell für Service-Schicht-Komponenten verwendet. Sie kennzeichnet Klassen, die Geschäftslogik enthalten, und fördert damit die Trennung von Geschäftslogik und technischer Infrastruktur.

@Service
public class MyService {
    // Geschäftslogik
}

9.3.2.3 @Repository

Die @Repository-Annotation ist speziell für Data-Access-Objekte (DAOs) vorgesehen. Sie abstrahiert die Datenbankzugriffsoperationen und ermöglicht es Spring, spezifische Data-Access-Exceptions in die allgemeinen Spring-Unchecked-Data-Access-Exceptions umzuwandeln.

@Repository
public class MyRepository {
    // Datenzugriffslogik
}

9.3.2.4 @Controller

Die @Controller-Annotation wird in Web-Anwendungen für Controller-Komponenten verwendet, die HTTP-Anfragen bearbeiten. In Kombination mit Spring MVC ermöglicht diese Annotation die Erstellung von Web-Anwendungen mit Model-View-Controller-Architektur.

@Controller
public class MyController {
    // HTTP-Anfragen Handling
}

9.3.2.5 Diagramm: Stereotyp-Annotations im Spring Framework

Dieses Diagramm zeigt, wie @Component als grundlegende Annotation dient, von der @Service, @Repository, und @Controller ableiten. Jede dieser spezifischen Annotations fokussiert sich auf einen bestimmten Bereich innerhalb der Anwendung.

Die spezifischen Stereotyp-Annotations in Spring (@Component, @Service, @Repository, und @Controller) bieten eine kraftvolle Möglichkeit, die Rollen und Verantwortlichkeiten von Komponenten innerhalb der Anwendung zu definieren und zu differenzieren. Durch die Verwendung dieser Annotations können Entwickler den Anwendungskontext sauber strukturieren, was zu einer verbesserten Lesbarkeit, Wartbarkeit und Testbarkeit des Codes führt.

9.3.3 Conditional Beans: Einsatz von @Conditional für bedingte Bean-Erstellung

Die @Conditional Annotation in Spring ermöglicht es Entwicklern, die Erstellung von Beans an bestimmte Bedingungen zu knüpfen. Dies eröffnet vielfältige Möglichkeiten für eine dynamischere und flexiblere Konfiguration, indem Beans nur dann instanziiert werden, wenn spezifische Kriterien erfüllt sind.

9.3.3.1 Grundlagen von @Conditional

@Conditional wird in Verbindung mit einer oder mehreren Implementierungen des Condition-Interfaces verwendet. Diese Implementierungen definieren die Logik, die bestimmt, ob eine Bean erstellt werden soll oder nicht. @Conditional kann sowohl auf der Ebene der Bean-Definition in einer @Configuration-Klasse als auch auf ganzen Klassen angewendet werden.

9.3.3.2 Beispiel einer einfachen Conditional Bean

@Configuration
public class AppConfig {

    @Bean
    @Conditional(MyCondition.class)
    public MyBean conditionalBean() {
        return new MyBean();
    }
}

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return System.getProperty("my.custom.condition") != null;
    }
}

In diesem Beispiel wird die Bean conditionalBean nur dann erstellt, wenn das System-Property my.custom.condition gesetzt ist.

9.3.3.3 Einsatzmöglichkeiten von @Conditional

Die Nutzung von @Conditional ist besonders nützlich in Szenarien, in denen die Konfiguration einer Anwendung von der Laufzeitumgebung abhängen soll, beispielsweise unterschiedliche Datenbankkonfigurationen für Entwicklungs- und Produktionsumgebungen oder das Aktivieren bestimmter Features basierend auf externen Konfigurationen.

9.3.3.4 Diagramm: Prozess der bedingten Bean-Erstellung

Das Diagramm veranschaulicht, wie durch die @Conditional Annotation die Erstellung einer Bean von der Auswertung einer Bedingung abhängig gemacht wird.

Die @Conditional Annotation stellt eine mächtige Ergänzung des Spring Frameworks dar, die eine feingranulare Steuerung der Bean-Erstellung basierend auf benutzerdefinierten Bedingungen ermöglicht. Durch ihren Einsatz können Entwickler ihre Anwendungskonfiguration weiter dynamisieren und anpassen, sodass Beans nur unter den gewünschten Umständen instanziiert werden. Dies fördert die Entwicklung modularer und flexibler Anwendungen, die sich effizient an verschiedene Betriebsumgebungen anpassen lassen.

9.4 Teil 3: Praktische Übung

9.4.1 Spring-Projekt Einrichtung: Schritt-für-Schritt-Anleitung

Die Einrichtung eines minimalen Spring-Projekts kann in wenigen Schritten erfolgen. Wir unterscheiden dabei zwischen der Einrichtung in IntelliJ IDEA und Visual Studio Code.

9.4.2 Variante 1: IntelliJ IDEA

9.4.2.1 Schritt 1: Spring Initializr

9.4.2.2 Schritt 2: Projekt-Abhängigkeiten

9.4.2.3 Schritt 3: Projektname und -ort

9.4.2.4 Schritt 4: SpringAnwendung starten

9.4.3 Variante 2: Visual Studio Code

9.4.3.1 Schritt 1: Voraussetzungen

9.4.3.2 Schritt 2: Spring Initializr

9.4.3.3 Schritt 3: Projekt öffnen

9.4.3.4 Schritt 4: Spring-Anwendung starten

9.4.3.5 Gemeinsame Schritte nach dem Start

Unabhängig von der IDE solltest du jetzt eine laufende Spring-Anwendung haben, die auf localhost:8080 erreichbar ist. Für weitergehende Anpassungen und Erweiterungen der Anwendung kannst du zusätzliche Abhängigkeiten und Konfigurationen hinzufügen.

9.4.4 Anwendungsbeispiel: Entwicklung einer kleinen Spring-Anwendung

In diesem Beispiel entwickeln wir eine einfache Spring-Anwendung, die verschiedene Konfigurationstechniken nutzt. Wir gehen davon aus, dass das Projekt bereits erstellt wurde, wie in der vorherigen Anleitung beschrieben.

9.4.4.1 Schritt 1: Einrichtung einer Java-basierten Konfiguration

  1. Erstelle eine Konfigurationsklasse: Im src/main/java Verzeichnis deines Projekts erstelle eine neue Java-Klasse namens AppConfig.
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;

@Configuration
@ComponentScan(basePackages = "com.example.myapp")
public class AppConfig {
    // Weitere Konfigurationen und Bean-Definitionen können hier hinzugefügt werden
}
  1. Aktiviere den Komponentenscan: Durch @ComponentScan teilen wir Spring mit, wo es nach weiteren Komponenten/Beans suchen soll. basePackages definiert das Paket, in dem Spring nach Beans suchen soll.

9.4.4.2 Schritt 2: Definition von Beans und Durchführung eines Komponentenscans

  1. Definiere eine Komponente: Erstelle eine neue Klasse MyService im Paket com.example.myapp.
package com.example.myapp;

import org.springframework.stereotype.Service;

@Service
public class MyService {
    public String sayHello() {
        return "Hello, Spring!";
    }
}

Die @Service Annotation markiert diese Klasse als eine Bean, die vom Spring IoC-Container verwaltet wird, dank des Komponentenscans in AppConfig.

9.4.4.3 Schritt 3: Anwendung von Profilen und bedingten Beans

  1. Erstelle ein Profil-spezifisches Bean: Füge der AppConfig-Klasse eine bedingte Bean hinzu, die nur unter einem bestimmten Profil verfügbar ist.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;

public class AppConfig {

    @Bean
    @Profile("development")
    public MyBean devMyBean() {
        return new MyBean("Entwicklungsmodus Bean");
    }
}
  1. Erstelle ein bedingtes Bean: Implementiere eine Bedingungsklasse und verwende die @Conditional Annotation, um ein Bean nur unter bestimmten Bedingungen zu erstellen.
import org.springframework.context.annotation.Conditional;

@Bean
@Conditional(MyCustomCondition.class)
public MyBean conditionalBean() {
    return new MyBean("Bedingtes Bean");
}

public class MyCustomCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Implementiere die Logik, die bestimmt, ob das Bean erstellt werden soll
        return true; // Beispiel: Immer true für die Demonstration
    }
}

9.4.5 Anwendungsstart und Test

Dieses einfache Beispiel demonstriert, wie du eine Spring-Anwendung mit verschiedenen Konfigurationstechniken wie Java-basierter Konfiguration, Komponentenscans, Profilen und bedingten Beans einrichten kannst. Diese Techniken bieten eine solide Grundlage für die Entwicklung flexibler und umgebungsspezifischer Anwendungskonfigurationen.