Anotação @ Lookup na primavera

Anotação @ Lookup na primavera

1. Introdução

Neste tutorial rápido, vamos dar uma olhada no suporte de injeção de dependência de nível de método do Spring, por meio da anotação@Lookup.

2. Por que@Lookup?

Um método anotado com@Lookup diz ao Spring para retornar uma instância do tipo de retorno do método quando o invocarmos.

Essencialmente, Spring irá sobrescrever nosso método anotado e usar o tipo de retorno e parâmetros de nosso método como argumentos paraBeanFactory#getBean.

@Lookup é útil para:

  • Injetando um bean com escopo de protótipo em um bean singleton (semelhante aProvider)

  • Injetando dependências processualmente

Observe também que@Lookup é o equivalente Java do elemento XMLlookup-method.

3. Usando@Lookup

3.1. Injetando bean com escopo de protótipo em um bean singleton

Se decidirmos ter um protótipo de bean Spring, somos quase imediatamente confrontados com o problema de como nossos Spring beans singleton acessarão esses protótipos Spring beans?

Agora,Provider é certamente uma maneira, embora@Lookup seja mais versátil em alguns aspectos.

Primeiro, vamos criar um bean de protótipo que posteriormente injetaremos em um bean singleton:

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

E se criarmos um singleton bean que usa@Lookup:

@Component
public class StudentServices {

    // ... member variables, etc.

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

    // ... getters and setters
}

Usando@Lookup, podemos obter uma instância deSchoolNotification por meio de nosso singleton bean:

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

Observe que emStudentServices, deixamos o métodogetNotification como um esboço.

Isso ocorre porque o Spring sobrescreve o método com uma chamada parabeanFactory.getBean(StudentNotification.class), então podemos deixá-lo vazio.

3.2. Injetando dependências de maneira processual

Ainda mais poderoso, porém, é que@Lookup nos permite injetar uma dependência proceduralmente, algo que não podemos fazer comProvider.

Vamos aprimorarStudentNotification com algum estado:

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

Agora, depende de algum contexto do Spring e também de um contexto adicional que forneceremos processualmente.

Podemos então adicionar um método aStudentServices que obtém os dados do aluno e os 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);
    }
}

Em tempo de execução, o Spring implementará o método da mesma maneira, com alguns truques adicionais.

Primeiro, observe que ele pode chamar um construtor complexo, bem como injetar outros beans Spring, permitindo-nos tratarSchoolNotification um pouco mais como um método ciente do Spring.

Ele faz isso implementandogetSchoolNotification com uma chamada parabeanFactory.getBean(SchoolNotification.class, name).

Em segundo lugar, às vezes podemos fazer o método@Lookup-annotated abstrato, como no exemplo acima.

Usarabstract é um pouco mais bonito do que um esboço, mas só podemos usá-lo quandodon’tcomponent-scan or @Bean-manage o bean circundante:

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

Com esta configuração, podemos adicionar dependências Spring, bem como dependências de método aSchoolNotification.

4. Limitações

Apesar da versatilidade de@Lookup, existem algumas limitações notáveis:

  • Métodos anotados em@Lookup, comogetNotification,, devem ser concretos quando a classe circundante, comoStudent,, é analisada por componente. Isso ocorre porque a varredura de componentes ignora os beans abstratos.

  • Métodos@Lookup-annotados não funcionarão quando a classe ao redor for gerenciada por@Bean.

Nessas circunstâncias, se precisarmos injetar um bean de protótipo em um singleton, podemos considerarProvider uma alternativa.

5. Conclusão

Neste artigo rápido, aprendemos como e quando usar a anotação@Lookup do Spring, incluindo como usá-la para injetar beans com escopo de protótipo em beans singleton e como usá-la para injetar dependências proceduralmente.

Todo o código usado para este tutorial pode ser encontradoover on Github.