@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.