@Lookup-Anmerkung im Frühling

@ Lookup Annotation im Frühjahr

1. Einführung

In diesem kurzen Tutorial werfen wir einen Blick auf die Unterstützung der Abhängigkeitsinjektion auf Methodenebene auf Spring-Ebene über die Annotation@Lookup.

2. Warum@Lookup?

Eine mit@Lookup annotierte Methode weist Spring an, eine Instanz des Rückgabetyps der Methode zurückzugeben, wenn wir sie aufrufen.

Im Wesentlichen überschreibt Spring unsere mit Anmerkungen versehene Methode und verwendet den Rückgabetyp und die Parameter unserer Methode als Argumente fürBeanFactory#getBean.

@Lookup ist nützlich für:

  • Injizieren einer Bean mit Prototyp-Gültigkeitsbereich in eine Singleton-Bean (ähnlich wieProvider)

  • Abhängigkeiten prozedural injizieren

Beachten Sie auch, dass@Lookup das Java-Äquivalent des XML-Elementslookup-method ist.

3. Verwenden von@Lookup

3.1. Injizieren einer Bean mit Prototyp-Gültigkeitsbereich in eine Singleton-Bean

Wenn wir uns zufällig für einen Spring Bean-Prototyp entscheiden, dann stehen wir fast sofort vor dem Problem, wie unsere Spring Beans mit Singleton auf diese Spring Beans-Prototypen zugreifen können.

Nun,Provider ist sicherlich eine Möglichkeit, obwohl@Lookup in mancher Hinsicht vielseitiger ist.

Erstellen wir zunächst eine Prototyp-Bean, die wir später in eine Singleton-Bean injizieren werden:

@Component
@Scope("prototype")
public class SchoolNotification {
    // ... prototype-scoped state
}

Und wenn wir eine Singleton-Bean erstellen, die@Lookup verwendet:

@Component
public class StudentServices {

    // ... member variables, etc.

    @Lookup
    public SchoolNotification getNotification() {
        return null;
    }

    // ... getters and setters
}

Mit@Lookup können wir eine Instanz vonSchoolNotification über unsere Singleton-Bean abrufen:

@Test
public void whenLookupMethodCalled_thenNewInstanceReturned() {
    // ... initialize context
    StudentServices first = this.context.getBean(StudentServices.class);
    StudentServices second = this.context.getBean(StudentServices.class);

    assertEquals(first, second);
    assertNotEquals(first.getNotification(), second.getNotification());
}

Beachten Sie, dass wir inStudentServices diegetNotification-Methode als Stub belassen haben.

Dies liegt daran, dass Spring die Methode mit einem Aufruf vonbeanFactory.getBean(StudentNotification.class) überschreibt, sodass wir sie leer lassen können.

3.2. Abhängigkeiten prozedural injizieren

Noch mächtiger ist jedoch, dass@Lookup es uns ermöglicht, eine Abhängigkeit prozedural einzufügen, was wir mitProvider nicht tun können.

Lassen Sie unsStudentNotification mit einem bestimmten Status verbessern:

@Component
@Scope("prototype")
public class SchoolNotification {
    @Autowired Grader grader;

    private String name;
    private Collection marks;

    public SchoolNotification(String name) {
        // ... set fields
    }

    // ... getters and setters

    public String addMark(Integer mark) {
        this.marks.add(mark);
        return this.grader.grade(this.marks);
    }
}

Nun ist es abhängig von einem Spring-Kontext und zusätzlichem Kontext, den wir prozedural bereitstellen.

Wir können dannStudentServices eine Methode hinzufügen, die Schülerdaten aufnimmt und beibehalten:

public abstract class StudentServices {

    private Map notes = new HashMap<>();

    @Lookup
    protected abstract SchoolNotification getNotification(String name);

    public String appendMark(String name, Integer mark) {
        SchoolNotification notification
          = notes.computeIfAbsent(name, exists -> getNotification(name)));
        return notification.addMark(mark);
    }
}

Zur Laufzeit wird Spring die Methode mit ein paar zusätzlichen Tricks auf die gleiche Weise implementieren.

Beachten Sie zunächst, dass es einen komplexen Konstruktor aufrufen und andere Spring Beans injizieren kann, sodass wirSchoolNotification ein bisschen mehr wie eine Spring-fähige Methode behandeln können.

Dazu wirdgetSchoolNotification mit einem Aufruf vonbeanFactory.getBean(SchoolNotification.class, name) implementiert.

Zweitens können wir manchmal die sannotierte Methode@Lookup-abstrakt machen, wie im obigen Beispiel.

Die Verwendung vonabstract sieht ein bisschen schöner aus als ein Stub, aber wir können es nur verwenden, wenn wirdon’tcomponent-scan or @Bean-manage die umgebende Bohne sind:

@Test
public void whenAbstractGetterMethodInjects_thenNewInstanceReturned() {
    // ... initialize context

    StudentServices services = context.getBean(StudentServices.class);
    assertEquals("PASS", services.appendMark("Alex", 89));
    assertEquals("FAIL", services.appendMark("Bethany", 78));
    assertEquals("PASS", services.appendMark("Claire", 96));
}

Mit diesem Setup können wirSchoolNotification sowohl Spring-Abhängigkeiten als auch Methodenabhängigkeiten hinzufügen.

4. Einschränkungen

Trotz der Vielseitigkeit von@Lookupgibt es einige bemerkenswerte Einschränkungen:

  • @Lookup-annotierte Methoden wiegetNotification, müssen konkret sein, wenn die umgebende Klasse wieStudent, von Komponenten gescannt wird. Dies liegt daran, dass die Komponentenprüfung abstrakte Beans überspringt.

  • @Lookup-annotierte Methoden funktionieren überhaupt nicht, wenn die umgebende Klasse@Bean-verwaltet ist.

Wenn wir unter diesen Umständen eine Prototyp-Bean in einen Singleton injizieren müssen, können wir alternativ nachProvider suchen.

5. Fazit

In diesem kurzen Artikel haben wir gelernt, wie und wann die Annotation@Lookupvon Spring verwendet wird, einschließlich der Verwendung zum Injizieren von Prototyp-Beans in Singleton-Beans und zum prozeduralen Injizieren von Abhängigkeiten.

Der gesamte für dieses Tutorial verwendete Code befindet sich inover on Github.