Skip to main content
Entwickler Themen
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Injection: Regeln und Diagnose

Komponenten erkennen

Der Scanner sucht jakarta.inject.Singleton und jakarta.inject.Named auf Typen. Beide bedeuten hier dasselbe: eine bekannte Klasse mit Singleton-Scope. Der Wert von @Named wird nicht ausgewertet; es gibt keine Suche nach Namen.

Die Reflections-Abfrage kann auch Unterklassen eines annotierten Typs innerhalb des Scan-Pakets liefern. Beabsichtigte konkrete Klassen sollten deshalb explizit annotiert werden. Es gibt kein eigenes @Component-Stereotyp, auch wenn ältere Quellkommentare den Begriff verwenden.

@Qualifier, Provider, Instance, Producer, @PostConstruct und @PreDestroy werden nicht verarbeitet. @Named an einem Parameter oder Feld ist kein Qualifier. Der Container vergleicht Raw-Typen nach Type Erasure; List<Address> sucht also nach List.

Scan-Konfiguration

new ApplicationContext("example.services");
new ApplicationContext(MyConfiguration.class);

Der String bezeichnet Paket und Unterpakete. Bei einer Konfigurationsklasse verwendet @Config(basePackage = "...") ein explizites Paket. Ohne Annotation oder bei leerem Wert gilt configClass.getPackageName(). Die Annotation wird nicht von einer Oberklasse geerbt. @Config bindet keine Konfigurationswerte, sondern bestimmt nur die Scan-Wurzel.

Normale Auflösungsfehler werden nicht unmittelbar als fehlgeschlagener Kontextkonstruktor gemeldet. Unauflösbare Klassen fehlen später im Kontext. ComponentScanner.getErrors() liefert die Erklärung; ein späteres getComponent kann nur No component found for type: melden.

Konstruktor, Felder und Setter

Der Scanner wählt genau einen Konstruktor:

  1. Mehrere @Inject-Konstruktoren machen die Klasse ungültig.
  2. Genau ein @Inject-Konstruktor hat Vorrang.
  3. Sonst wird der einzige deklarierte Konstruktor verwendet, auch mit Parametern und ohne Annotation.
  4. Bei mehreren Konstruktoren wird ein parameterloser gewählt, sofern vorhanden.
  5. Andernfalls ist die Klasse nicht instanziierbar.

Nach der Konstruktion folgen die deklarierten @Inject-Felder, danach gültige @Inject-Setter. Ein Setter muss void zurückgeben, genau einen Parameter besitzen und mit set beginnen. Andere annotierte Methoden werden als Fehler gemeldet und übersprungen; die Klasse kann trotzdem entstehen.

Nur auf der Klasse selbst deklarierte Felder und Methoden werden berücksichtigt, keine geerbten. Die Mitglieder werden reflektiv zugänglich gemacht und müssen nicht öffentlich sein. In benannten Modulen sind dafür passende opens erforderlich.

Instanzen anfordern

public <T> T getComponent(Class<? extends T> type)
public <T> T createComponent(Class<? extends T> type)
public <T> void addSingleton(Class<? extends T> type, T instance)

getComponent erzeugt eine Singleton-Instanz beim ersten Zugriff und liefert danach genau diese zurück. createComponent erzeugt immer eine neue Instanz; deren Abhängigkeiten kommen weiterhin aus getComponent und bleiben geteilt. Das ist keine rekursive Neuerzeugung des vollständigen Objektgraphen.

Unbekannte Typen führen zu IllegalArgumentException mit No component found for type:. Fehler beim Konstruktoraufruf oder beim Schreiben eines Members werden als RuntimeException mit Fehler beim Erstellen der Instanz für und ursprünglicher Ursache eingepackt.

Der Kontext ist eine einfache Map ohne zugesicherte Thread-Sicherheit. Er hat keine Shutdown- oder List-all-API. Ressourcen gehören der Anwendung. Manuell registrierte Instanzen werden nicht nachträglich injiziert und später nicht automatisch geschlossen.

Interfaces, Oberklassen und Konflikte

Jede konkrete Klasse ist ein Schlüssel. Zusätzlich werden ihre Oberklassen bis vor Object und direkt deklarierten Interfaces abgebildet, sofern genau eine Component diesen Supertyp besitzt. Alle solchen Schlüssel zeigen auf dieselben ComponentData und damit dieselbe Instanz.

Mehrere Implementierungen machen einen Supertyp mehrdeutig; er wird nicht als Schlüssel eingetragen. Die konkreten Klassen bleiben erreichbar. Nur über eine Oberklasse geerbte Interfaces werden nicht rekursiv ergänzt.

Die Abhängigkeitsauflösung erfolgt vor der Instanzerzeugung. Fehlende Abhängigkeiten, Konflikte und Zyklen verhindern die Aufnahme in die instanziierbare Map. Bei einem Zyklus erscheinen die jeweiligen Abhängigkeiten als fehlend.

ComponentScanner scanner = new ComponentScanner("example.services");
scanner.getInstantiableComponents();
scanner.getNotUniqueTypes();
scanner.getErrors();

Diese Map und Collections gehören dem Scanner und sind keine unveränderlichen Kopien. Für Diagnosezwecke lesen, nicht beiläufig verändern.

Fehlermeldungen

Situation Meldung beziehungsweise Folge
Mehrere markierte Konstruktoren Multiple @Inject constructors found for ...
Keine eindeutige Auswahl, kein parameterloser Konstruktor No @Inject constructor and no no-arg constructor found for ...
Ungeeignete annotierte Methode Invalid @Inject method detected in ...; Methode wird übersprungen
Fehlender Abhängigkeitstyp Component cannot be instantiated: ..., dazu Missing constructor dependencies, Missing field dependencies oder Missing setter dependencies
Mehrdeutige Supertypen Derselbe Präfix mit Conflicting super types detected
Typ fehlt beim Zugriff No component found for type: ...

Ein einziger Konstruktor mit String-Parameter wird beispielsweise gewählt, auch ohne @Inject. Da String keine Component ist, fehlt diese Abhängigkeit und die Klasse wird ausgelassen. Eine spätere manuelle Registrierung repariert diese frühere Scan-Entscheidung nicht.

Eigene Instanzen registrieren

addSingleton(type, instance) verhält sich abhängig vom bestehenden Schlüssel:

Vorhandener Eintrag Ergebnis
Typ fehlt Instanz wird eingetragen und von getComponent geliefert
Gescanntes konkretes Component-Objekt Austausch scheitert mit Type cannot be replaced:
Alias auf eindeutigen Supertyp Nur dieser Alias wird ersetzt; der konkrete Klassenschlüssel kann weiterhin die frühere Instanz liefern

Es erfolgt kein erneuter Scan und keine Member-Injection auf der gelieferten Instanz. createComponent funktioniert für eine rein manuell eingetragene Instanz ohne hinterlegte Konstruktormetadaten nicht.

Scope und ComponentData

Scope kennt SINGLETON und PROTOTYPE. Der Scanner weist jedoch ausschließlich SINGLETON zu. Für eine neue Instanz einer gescannten Klasse dient createComponent, nicht eine zusätzliche Scope-Annotation.

ComponentData ist die öffentliche Low-Level-Repräsentation: Typ, Scope, Konstruktor, injizierte Felder und Setter sowie gegebenenfalls Instanz. Konstruktoren nehmen Typ/Scope, eine vorhandene Instanz oder vollständige Erzeugungsmetadaten entgegen. Memberlisten werden beim Aufbau kopiert. setInstance speichert nur für SINGLETON und lehnt eine zweite Zuweisung ab; PROTOTYPE wird nicht gecacht.

Diese Metadaten sind kein nachträglicher Konfigurations-Builder für einen existierenden Kontext. Anwendungscode verwendet in der Regel ApplicationContext; ComponentScanner und ComponentData helfen bei Diagnose und gezielter Low-Level-Integration.