@Lookup Аннотация весной

@Lookup Аннотация весной

1. Вступление

В этом кратком руководстве мы рассмотрим поддержку внедрения зависимостей Spring на уровне метода с помощью аннотации@Lookup.

2. Почему@Lookup?

Метод, помеченный@Lookup, сообщает Spring, что нужно вернуть экземпляр типа возвращаемого метода, когда мы его вызываем.

По сути, Spring переопределит наш аннотированный метод и будет использовать возвращаемый тип и параметры нашего метода в качестве аргументов дляBeanFactory#getBean..

@Lookup полезен для:

  • Внедрение компонента с областью действия прототипа в одноэлементный компонент (аналогичноProvider)

  • Инъекция зависимостей процедурно

Также обратите внимание, что@Lookup - это Java-эквивалент элемента XMLlookup-method.

3. Используя@Lookup

3.1. Внедрение компонента с областью видимости прототипа в одноэлементный компонент

Если мы решим иметь прототип Spring EJB, то мы почти сразу же столкнемся с проблемой, как наши одноэлементные Spring-бины получат доступ к этим прототипам Spring?

ТеперьProvider, безусловно, односторонний, хотя@Lookup в некоторых отношениях более универсален.

Во-первых, давайте создадим компонент-прототип, который позже мы внедрим в одноэлементный компонент:

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

И если мы создадим одноэлементный компонент, который использует@Lookup:

@Component
public class StudentServices {

    // ... member variables, etc.

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

    // ... getters and setters
}

Используя@Lookup, мы можем получить экземплярSchoolNotification через наш 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());
}

Обратите внимание, что вStudentServices мы оставили методgetNotification как заглушку.

Это связано с тем, что Spring переопределяет метод вызовомbeanFactory.getBean(StudentNotification.class), поэтому мы можем оставить его пустым.

3.2. Процедурное внедрение зависимостей

Еще более мощным является то, что@Lookup позволяет нам процедурно внедрить зависимость, чего мы не можем сделать сProvider.

Давайте улучшимStudentNotification некоторым состоянием:

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

Теперь это зависит от некоторого контекста Spring, а также дополнительного контекста, который мы предоставим процедурно.

Затем мы можем добавить кStudentServices метод, который принимает данные об учениках и сохраняет их:

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

Во время выполнения Spring реализует метод таким же образом, с парой дополнительных уловок.

Во-первых, обратите внимание, что он может вызывать сложный конструктор, а также внедрять другие bean-компоненты Spring, что позволяет нам рассматриватьSchoolNotification немного больше как метод, поддерживающий Spring.

Это достигается путем реализацииgetSchoolNotification с вызовомbeanFactory.getBean(SchoolNotification.class, name).

Во-вторых, мы можем иногда сделать саннотированный метод@Lookup-абстрактным, как в приведенном выше примере.

Использованиеabstract выглядит немного лучше, чем заглушка, но мы можем использовать его только тогда, когда мыdon’tcomponent-scan or @Bean-manage окружающий bean-компонент:

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

С помощью этой настройки мы можем добавить зависимости Spring, а также зависимости методов вSchoolNotification.

4. Ограничения

Несмотря на универсальность@Lookup, есть несколько заметных ограничений:

  • Аннотированные методы@Lookup, такие какgetNotification,, должны быть конкретными, когда окружающий класс, напримерStudent,, сканируется по компонентам. Это связано с тем, что компонентное сканирование пропускает абстрактные компоненты.

  • Саннотированные методы@Lookup-не будут работать вообще, если окружающий класс находится под управлением@Bean.

В таких обстоятельствах, если нам нужно внедрить компонент-прототип в синглтон, мы можем обратиться кProvider в качестве альтернативы.

5. Заключение

В этой быстрой статье мы узнали, как и когда использовать аннотацию Spring@Lookup, в том числе о том, как использовать ее для внедрения bean-компонентов с прототипом в одноэлементные bean-компоненты и как использовать их для процедурного внедрения зависимостей.

Весь код, используемый в этом руководстве, можно найти вover on Github.