Annotation @ Lookup au printemps

@Lookup Annotation in Spring

1. introduction

Dans ce rapide didacticiel, nous allons examiner la prise en charge de l'injection de dépendances au niveau de la méthode de Spring, via l'annotation@Lookup.

2. Pourquoi@Lookup?

Une méthode annotée avec@Lookup indique à Spring de renvoyer une instance du type de retour de la méthode lorsque nous l'appelons.

Essentiellement, Spring remplacera notre méthode annotée et utilisera le type de retour et les paramètres de notre méthode comme arguments pourBeanFactory#getBean.

@Lookup est utile pour:

  • Injection d'un bean à portée prototype dans un bean singleton (similaire àProvider)

  • Injection de dépendances de manière procédurale

Notez également que@Lookup est l'équivalent Java de l'élément XMLlookup-method.

3. Utilisation de@Lookup

3.1. Injection d'un haricot à portée prototype dans un haricot singleton

Si nous décidons d’avoir un prototype de haricot Spring, nous nous trouverons presque immédiatement face au problème de savoir comment nos haricots Spring singleton auront accès à ces prototypes de haricots Spring.

Maintenant,Provider est certainement un moyen, bien que@Lookup soit plus polyvalent à certains égards.

Commençons par créer un bean prototype que nous injecterons plus tard dans un bean singleton:

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

Et si nous créons un bean singleton qui utilise@Lookup:

@Component
public class StudentServices {

    // ... member variables, etc.

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

    // ... getters and setters
}

En utilisant@Lookup, nous pouvons obtenir une instance deSchoolNotification via notre bean singleton:

@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());
}

Notez que dansStudentServices, nous avons laissé la méthodegetNotification comme un stub.

En effet, Spring remplace la méthode par un appel àbeanFactory.getBean(StudentNotification.class), nous pouvons donc la laisser vide.

3.2. Injection de dépendances de manière procédurale

Plus puissant encore, cependant, est que@Lookup nous permet d'injecter une dépendance de manière procédurale, ce que nous ne pouvons pas faire avecProvider.

AmélioronsStudentNotification avec un état:

@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);
    }
}

À présent, cela dépend du contexte Spring et du contexte supplémentaire que nous allons fournir de manière procédurale.

Nous pouvons ensuite ajouter une méthode àStudentServices qui prend les données des étudiants et les persiste:

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);
    }
}

Au moment de l'exécution, Spring implémentera la méthode de la même manière, avec quelques astuces supplémentaires.

Tout d'abord, notez qu'il peut appeler un constructeur complexe ainsi qu'injecter d'autres beans Spring, ce qui nous permet de traiterSchoolNotification un peu plus comme une méthode prenant en charge Spring.

Il le fait en implémentantgetSchoolNotification avec un appel àbeanFactory.getBean(SchoolNotification.class, name).

Deuxièmement, nous pouvons parfois rendre la méthode annotée@Lookup-abstraite, comme dans l'exemple ci-dessus.

Utiliserabstract est un peu plus joli qu'un stub, mais nous ne pouvons l'utiliser que lorsque nousdon’tcomponent-scan or @Bean-manage le bean environnant:

@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));
}

Avec cette configuration, nous pouvons ajouter des dépendances Spring ainsi que des dépendances de méthode àSchoolNotification.

4. Limites

Malgré la polyvalence de@Lookup, il existe quelques limitations notables:

  • Les méthodes annotées@Lookup, commegetNotification,, doivent être concrètes lorsque la classe environnante, commeStudent,, est balayée par les composants. En effet, l'analyse des composants ignore les beans abstraits.

  • Les méthodes annotées@Lookup-ne fonctionnent pas du tout lorsque la classe environnante est gérée par@Bean.

Dans ces circonstances, si nous devons injecter un bean prototype dans un singleton, nous pouvons nous tourner versProvider comme alternative.

5. Conclusion

Dans cet article rapide, nous avons appris comment et quand utiliser l'annotation@Lookupde Spring, y compris comment l'utiliser pour injecter des beans de type prototype dans des beans singleton et comment l'utiliser pour injecter des dépendances de manière procédurale.

Tout le code utilisé pour ce tutoriel peut être trouvéover on Github.