1. Введение
В этом руководстве мы рассмотрим различные варианты метода BeanFactory.getBean()
.
Проще говоря, как следует из названия метода, он отвечает за извлечение экземпляра компонента из контейнера Spring .
2. Настройка бинов Spring
Во-первых, давайте определим несколько компонентов Spring для тестирования. Есть несколько способов, которыми мы можем предоставить определения bean-компонентов для контейнера Spring, но в нашем примере мы будем использовать конфигурацию Java на основе аннотаций:
@Configuration
class AnnotationConfig {
@Bean(name = {"tiger", "kitty"})
@Scope(value = "prototype")
Tiger getTiger(String name) {
return new Tiger(name);
}
@Bean(name = "lion")
Lion getLion() {
return new Lion("Hardcoded lion name");
}
interface Animal {}
}
Мы создали два компонента. Lion
имеет одноэлементную область действия по умолчанию. Tiger
явно настроен на область действия прототипа . Кроме того, обратите внимание, что мы определили имена для каждого компонента, которые мы будем использовать в дальнейших запросах.
3. API getBean()
BeanFactory
предоставляет пять различных сигнатур метода getBean()
, которые мы рассмотрим в следующих подразделах.
3.1. Получение компонента по имени
Давайте посмотрим, как мы можем получить экземпляр компонента Lion , используя его имя:
Object lion = context.getBean("lion");
assertEquals(Lion.class, lion.getClass());
В этом варианте мы предоставляем имя, а взамен получаем экземпляр класса Object
, если в контексте приложения существует компонент с данным именем. В противном случае и эта, и все другие реализации выдают исключение NoSuchBeanDefinitionException
, если поиск компонента завершается неудачно.
Главный недостаток в том, что после извлечения бина мы должны привести его к нужному типу . Это может привести к еще одному исключению , если возвращаемый компонент имеет тип, отличный от ожидаемого .
Предположим, мы пытаемся получить Тигра
, используя имя «лев».
Когда мы передаем результат Tiger
, он выдает ClassCastException
:
assertThrows(ClassCastException.class, () -> {
Tiger tiger = (Tiger) context.getBean("lion");
});
3.2. Получение компонента по имени и типу
Здесь нам нужно указать как имя, так и тип запрашиваемого компонента:
Lion lion = context.getBean("lion", Lion.class);
По сравнению с предыдущим методом этот безопаснее, потому что мы получаем информацию о несоответствии типов мгновенно:
assertThrows(BeanNotOfRequiredTypeException.class, () ->
context.getBean("lion", Tiger.class));
}
3.3. Получение компонента по типу
В третьем варианте getBean()
достаточно указать только тип компонента:
Lion lion = context.getBean(Lion.class);
В этом случае нам нужно обратить особое внимание на потенциально неоднозначный результат :
assertThrows(NoUniqueBeanDefinitionException.class, () ->
context.getBean(Animal.class));
}
В приведенном выше примере, поскольку и Lion
, и Tiger
реализуют интерфейс Animal
, простого указания типа недостаточно для однозначного определения результата. Поэтому мы получаем NoUniqueBeanDefinitionException
.
3.4. Получение компонента по имени с параметрами конструктора
В дополнение к имени компонента мы также можем передать параметры конструктора:
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Этот метод немного отличается, потому что он применяется только к bean-компонентам с областью действия прототипа .
В случае с синглтонами мы получим исключение BeanDefinitionStoreException .
Поскольку прототип bean-компонента будет возвращать вновь созданный экземпляр каждый раз, когда он запрашивается из контейнера приложения, мы можем предоставлять параметры конструктора на лету при вызове getBean()
:
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Tiger secondTiger = (Tiger) context.getBean("tiger", "Striped");
assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());
Как мы видим, каждый Tiger
получает другое имя в соответствии с тем, что мы указали в качестве второго параметра при запросе bean-компонента.
3.5. Получение компонента по типу с параметрами конструктора
Этот метод аналогичен предыдущему, но нам нужно передать тип вместо имени в качестве первого аргумента:
Tiger tiger = context.getBean(Tiger.class, "Shere Khan");
assertEquals("Shere Khan", tiger.getName());
Подобно извлечению компонента по имени с параметрами конструктора, этот метод применяется только к компонентам с областью действия прототипа .
4. Рекомендации по использованию
Несмотря на то, что он определен в интерфейсе BeanFactory , доступ к
методу getBean()
чаще всего осуществляется через ApplicationContext.
Как правило, мы не хотим использовать метод getBean()
непосредственно в нашей программе .
Beans должен управляться контейнером. Если мы хотим использовать один из них, мы должны полагаться на внедрение зависимостей, а не на прямой вызов ApplicationContext.getBean()
.
Таким образом, мы можем избежать смешивания логики приложения с деталями, связанными с фреймворком.
5. Вывод
В этом кратком руководстве мы рассмотрели все реализации метода getBean()
из интерфейса BeanFactory
и описали плюсы и минусы каждой из них.
Все приведенные здесь примеры кода доступны на GitHub .