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 .