Определение бина - это, по сути, набор правил для создания одного или нескольких объектов. В ответ на запрос контейнер просматривает набор правил именованного бина и использует конфигурационные метаданные, заключенные в определении этого бина, для создания (или получения) фактического объекта.
Если вы используете конфигурационные метаданные на основе XML, вы указываете тип (или класс) объекта, экземпляр которого нужно создать, в атрибуте class
элемента <bean/>
. Этот атрибут class
(который внутренне является свойством Class
экземпляра BeanDefinition
) как правило является обязательным. Свойство Class
можно использовать одним из двух способов:
-
Как правило, для задания класса бина, который должен быть построен в случае, если контейнер сам непосредственно создает бин путем рефлексивного вызова его конструктора, что в некоторой степени эквивалентно коду Java с оператором
new
. -
Для задания фактического класса, содержащего
статический
фабричный метод, который вызывается для создания объекта, в менее распространенном случае, когда контейнер вызываетстатический
фабричный метод на классе для создания бина. Тип объекта, возвращаемый в результате вызовастатического
фабричного метода, может быть тем же классом или совсем другим классом.
Создание экземпляра с помощью конструктора
Если бин создается с помощью конструктора, могут быть использованы все стандартные классы, и они будут совместимы со Spring. То есть разрабатываемый класс не обязательно должен реализовывать какие-либо конкретные интерфейсы или быть написанным определенным образом. Простого задания класса бина должно быть достаточно. Однако в зависимости от того, какой тип IoC используется для данного конкретного бина, может понадобиться конструктор по умолчанию (пустой).
IoC-контейнер Spring может управлять практически любым классом, которым вам необходимо управлять. Он не ограничивается управлением истинными JavaBeans. Большинство пользователей Spring предпочитают фактические JavaBeans с использованием конструктора по умолчанию (без аргументов) и соответствующими сеттерами и геттерами, смоделированными по свойствам контейнера. В вашем контейнере также могут присутствовать более экзотические классы не в стиле бинов. Если, например, нужно использовать устаревший пул соединений, который абсолютно не соответствует спецификации JavaBean, Spring может управлять и им.
С помощью конфигурационных метаданных на основе XML можно задать свой класс бина следующим образом:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
Подробнее о механизме предоставления аргументов конструктору (если требуется) и установки свойств экземпляра объекта после того, как объект построен, см. в разделе Внедрение зависимостей.
Создание экземпляра с помощью статического фабричного метода
При определении бина, который вы создаете с помощью статического фабричного метода, используйте атрибут class
для задания класса, который содержит статический
фабричный метод, и атрибут factory-method
для задания имени самого фабричного метода. Вам должна быть доступна возможность вызвать этот метод (с необязательными аргументами, как описано далее) и возвращать работающий объект, который впоследствии будет рассматриваться так, как если бы он был создан с помощью конструктора. Одним из вариантов использования такого определения бина является вызов статических
фабрик в унаследованном коде.
Следующее определение бина указывает, что бин будет создан путем вызова метода фабрики. В определении указывается не тип (класс) возвращаемого объекта, а только класс, содержащий фабричный метод. В этом примере метод createInstance()
должен быть статическим
. В следующем примере показано, как задать фабричный метод:
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
В следующем примере показан класс, который будет работать с предыдущим определением бина:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
class ClientService private constructor() {
companion object {
private val clientService = ClientService()
@JvmStatic
fun createInstance() = clientService
}
}
Подробнее о механизме предоставления (необязательных) аргументов фабричному методу и установки свойств экземпляра объекта после возврата объекта из фабрики.
Создание экземпляра с помощью экземплярного фабричного метода
Подобно созданию экземпляра через статический фабричный метод, создание экземпляра с помощью экземплярного фабричного метода вызывает нестатический метод существующего бина из контейнера для создания нового бина. Чтобы использовать этот механизм, оставьте атрибут class
пустым, а в атрибуте factory-bean
укажите имя бина в текущем (или родительском, или предшествующем) контейнере, который содержит экземплярный метод, который будет вызван для создания объекта. Задайте имя самого фабричного метода с помощью атрибута factory-method
. В следующем примере показано, как сконфигурировать такой бин:
<!-- бин-фабрика, содержащий метод createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- внедрение любых зависимостей, необходимых этому бину-локатору -->
</bean>
<!-- бин, который будет создан с помощью бина-фабрики -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
В следующем примере показан соответствующий класс:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
class DefaultServiceLocator {
companion object {
private val clientService = ClientServiceImpl()
}
fun createClientServiceInstance(): ClientService {
return clientService
}
}
Один фабричный класс может содержать более одного фабричного метода, как показано в следующем примере:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- внедрение любых зависимостей, необходимых этому бину-локатору -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
В следующем примере показан соответствующий класс:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
class DefaultServiceLocator {
companion object {
private val clientService = ClientServiceImpl()
private val accountService = AccountServiceImpl()
}
fun createClientServiceInstance(): ClientService {
return clientService
}
fun createAccountServiceInstance(): AccountService {
return accountService
}
}
Данный подход демонстрирует, что самим бином-фабрикой можно управлять и конфигурировать его с помощью внедрения зависимостей (DI). См. раздел "Подробно о зависимостях и конфигурации".
FactoryBean
(обратите внимание на написание заглавными буквами) относится к специфическому для Spring классу реализации FactoryBean
.Определение типа бина во время выполнения
Определить тип конкретного бина во время выполнения - это нетривиальная задача. Заданный класс в определении метаданных бина - это просто исходная ссылка на класс, потенциально объединенный с объявленным фабричным методом или являющийся классом FactoryBean
, что может привести к получению другого типа бина во время выполнения, или вообще не заданный в случае фабричного метода на уровне экземпляра (который разрешается вместо этого через заданное имя factory-bean
). Кроме того, АОП-проксирование может поместить экземпляр бина в прокси на основе интерфейса с ограниченным открытием реального типа целевого бина (только его реализованные интерфейсы).
Рекомендуемый способ узнать фактический тип времени выполнения конкретного бина - это вызов BeanFactory.getType
для заданного имени бина. При этом учитываются все вышеперечисленные случаи и возвращается тип объекта, который будет возвращен вызовом BeanFactory.getBean
для того же имени бина.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ