Diese Seite bleibt auch ohne JavaScript lesbar. Suche und Buttons sind Zusatzkomfort.
Spring Core: DI, IoC, Beans und AOP
Das Fundament: ApplicationContext, Bean Lifecycle, Proxies, Querschnittsfunktionen und testbare Architektur.
1. IoC und Dependency Injection richtig verstehen
In klassischen Legacy-Anwendungen erzeugen Klassen oft ihre Abhängigkeiten selbst: new Service(), ServiceLocator.lookup(), statische Singletons oder direkte JNDI-Zugriffe. Dadurch entstehen versteckte Kopplungen. Spring dreht die Kontrolle um: Der Container erzeugt und verdrahtet Objekte. Die Klasse beschreibt ihre benötigten Abhängigkeiten über den Konstruktor.
Konstruktor-Injektion ist im Enterprise-Alltag meist die robusteste Variante: Pflichtabhängigkeiten sind sichtbar, Tests können einfache Fakes übergeben, und zirkuläre Abhängigkeiten fallen früh auf.
public class InvoiceService {
private final TaxClient taxClient = new TaxClient("https://tax.internal");
private final InvoiceRepository repository = new OracleInvoiceRepository();
public Invoice createInvoice(Order order) {
Money tax = taxClient.calculate(order.total());
Invoice invoice = Invoice.from(order, tax);
repository.save(invoice);
return invoice;
}
}
@Service
public class InvoiceService {
private final TaxClient taxClient;
private final InvoiceRepository repository;
public InvoiceService(TaxClient taxClient, InvoiceRepository repository) {
this.taxClient = taxClient;
this.repository = repository;
}
public Invoice createInvoice(Order order) {
Money tax = taxClient.calculate(order.total());
Invoice invoice = Invoice.from(order, tax);
repository.save(invoice);
return invoice;
}
}
2. Bean Lifecycle und Scopes
Singleton bedeutet in Spring: ein Bean-Objekt pro ApplicationContext, nicht global pro JVM. Deshalb müssen Singleton-Beans threadsicher entworfen werden. Mutable Felder mit Request-Zustand gehören nicht in Singleton-Services.
Prototype-Beans werden bei jeder Anfrage neu erzeugt, lösen aber selten Enterprise-Probleme. Für Web-Zustand gibt es Request- oder Session-Scopes; für fachliche Verarbeitung sollte Zustand möglichst über Methodenparameter laufen.
@Service
class PaymentService {
private String currentUser; // gefährlich: geteilter Zustand zwischen Requests
public Receipt pay(Command command) {
this.currentUser = command.user();
return process(command);
}
}
@Service
class PaymentService {
public Receipt pay(Command command, UserId userId) {
return process(command, userId);
}
}
3. AOP und Proxies
Spring nutzt Proxies für viele Querschnitte: Transaktionen, Security-Methodenregeln, Caching, Retry oder eigene Aspekte. Wichtig ist: Der Aufruf muss über den Proxy laufen. Self-Invocation innerhalb derselben Klasse umgeht häufig den Proxy und damit die Annotation.
AOP eignet sich für technische Querschnitte. Fachlogik sollte nicht in Aspekten versteckt werden, weil sie sonst schwer sichtbar, schwer testbar und schwer nachvollziehbar wird.
@Service
class OrderApplicationService {
@Transactional
public void placeOrder(PlaceOrder command) {
// Transaktion beginnt beim Proxy-Aufruf von außen
}
public void wrong() {
placeOrder(new PlaceOrder(...)); // Self-Invocation: proxy kann umgangen werden
}
}
Enterprise-Prüffragen
- Konstruktor-Injektion statt verstecktem Lookup?
- Services zustandsarm und thread-safe?
- Keine Fachlogik in AOP-Aspekten versteckt?
- Self-Invocation bei @Transactional vermieden?