Перейти к основному содержимому

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

· 4 мин. чтения

1. Введение

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

2. Почему @Lookup ?

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

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

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

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

Также обратите внимание, что @Lookup является Java-эквивалентом метода поиска XML-элементов .

3. Использование @Lookup

3.1. Внедрение Bean-компонента с областью действия прототипа в Singleton Bean

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

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

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

@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<Integer> 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<String, SchoolNotification> 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 реализует метод таким же образом, но с парой дополнительных приемов.

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

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

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

Использование abstract немного красивее, чем заглушка, но мы можем использовать его только тогда, когда мы не сканируем компоненты или не управляем @Bean окружающим 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 .

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

5. Вывод

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

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