1. введение
В этом уроке мы рассмотрим различные варианты метода BeanFactory.getBean () .
Проще говоря, как следует из названия метода, это отвечает за извлечение экземпляра компонента из контейнера Spring .
2. Установка весенних бобов
Во-первых, давайте определим несколько весенних бобов для тестирования. Существует несколько способов предоставления определений компонентов для контейнера Spring, но в нашем примере мы будем использовать Java config на основе аннотаций:
@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 bean, используя его имя:
Object lion = context.getBean("lion");
assertEquals(Lion.class, lion.getClass());В этом варианте мы предоставляем имя, а взамен получаем экземпляр Object class, если в контексте приложения существует компонент с заданным именем. В противном случае и эта, и все другие реализации выбрасывают 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");Этот метод немного отличается, потому что он применяется только к компонентам с областью действия прототипа .
В случае синглетов мы получим BeanDefinitionStoreException .
Поскольку компонент-прототип будет возвращать вновь созданный экземпляр каждый раз, когда он запрашивается из контейнера приложения, мы можем предоставить параметры конструктора на лету при вызове getBean() :
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Tiger secondTiger = (Tiger) context.getBean("tiger", "Striped");
assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());Как мы видим, каждый Tiger получает другое имя в соответствии с тем, что мы указали в качестве второго параметра при запросе компонента.
3.5. Получение Бина по Типу С Параметрами Конструктора
Этот метод аналогичен предыдущему, но нам нужно передать тип вместо имени в качестве первого аргумента:
Tiger tiger = context.getBean(Tiger.class, "Shere Khan");
assertEquals("Shere Khan", tiger.getName());Аналогично получению компонента по имени с параметрами конструктора, этот метод применяется только к компонентам с областью действия прототипа .
4. Соображения по использованию
Несмотря на то, что он определен в интерфейсе BeanFactory , метод getBean() чаще всего доступен через ApplicationContext. Как правило, мы не хотим использовать метод getBean() непосредственно в нашей программе .
Бобы должны управляться контейнером. Если мы хотим использовать один из них, мы должны полагаться на инъекцию зависимостей , а не на прямой вызов ApplicationContext.getBean() . Таким образом, мы можем избежать смешивания логики приложения с деталями, связанными с фреймворком.
5. Заключение
В этом кратком руководстве мы рассмотрели все реализации метода getBean() из интерфейса BeanFactory и описали плюсы и минусы каждого из них.
Все примеры кода, показанные здесь, доступны на GitHub .