Определение бина - это, по сути, набор правил для создания одного или нескольких объектов. В ответ на запрос контейнер просматривает набор правил именованного бина и использует конфигурационные метаданные, заключенные в определении этого бина, для создания (или получения) фактического объекта.

Если вы используете конфигурационные метаданные на основе XML, вы указываете тип (или класс) объекта, экземпляр которого нужно создать, в атрибуте class элемента <bean/>. Этот атрибут class (который внутренне является свойством Class экземпляра BeanDefinition) как правило является обязательным. Свойство Class можно использовать одним из двух способов:

  • Как правило, для задания класса бина, который должен быть построен в случае, если контейнер сам непосредственно создает бин путем рефлексивного вызова его конструктора, что в некоторой степени эквивалентно коду Java с оператором new.

  • Для задания фактического класса, содержащего статический фабричный метод, который вызывается для создания объекта, в менее распространенном случае, когда контейнер вызывает статический фабричный метод на классе для создания бина. Тип объекта, возвращаемый в результате вызова статического фабричного метода, может быть тем же классом или совсем другим классом.

Вложенные имена классов

Если вы хотите настроить определение бина для вложенного класса, вы можете использовать либо двоичное имя, либо исходное имя вложенного класса.

Например, если у вас есть класс SomeThing в пакете com.example, и этот класс SomeThing имеет статический вложенный класс, который называется OtherThing, они могут быть разделены знаком доллара($) или точкой (.). Таким образом, значение атрибута class в определении бина будет com.example.SomeThing$OtherThing или com.example.SomeThing.OtherThing.

Создание экземпляра с помощью конструктора

Если бин создается с помощью конструктора, могут быть использованы все стандартные классы, и они будут совместимы со 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"/>

В следующем примере показан класс, который будет работать с предыдущим определением бина:

Java
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}
    public static ClientService createInstance() {
        return clientService;
    }
}
Kotlin
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"/>

В следующем примере показан соответствующий класс:

Java
public class DefaultServiceLocator {
    private static ClientService clientService = new ClientServiceImpl();
    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
Kotlin
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"/>

В следующем примере показан соответствующий класс:

Java
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;
    }
}
Kotlin
class DefaultServiceLocator {
    companion object {
        private val clientService = ClientServiceImpl()
        private val accountService = AccountServiceImpl()
    }
    fun createClientServiceInstance(): ClientService {
        return clientService
    }
    fun createAccountServiceInstance(): AccountService {
        return accountService
    }
}

Данный подход демонстрирует, что самим бином-фабрикой можно управлять и конфигурировать его с помощью внедрения зависимостей (DI). См. раздел "Подробно о зависимостях и конфигурации".

В документации Spring "бин-фабрика (factory bean)" означает бин, который конфигурируется в контейнере Spring и создает объекты с помощью instance или static фабричного метода. И наоборот, FactoryBean (обратите внимание на написание заглавными буквами) относится к специфическому для Spring классу реализации FactoryBean.

Определение типа бина во время выполнения

Определить тип конкретного бина во время выполнения - это нетривиальная задача. Заданный класс в определении метаданных бина - это просто исходная ссылка на класс, потенциально объединенный с объявленным фабричным методом или являющийся классом FactoryBean, что может привести к получению другого типа бина во время выполнения, или вообще не заданный в случае фабричного метода на уровне экземпляра (который разрешается вместо этого через заданное имя factory-bean). Кроме того, АОП-проксирование может поместить экземпляр бина в прокси на основе интерфейса с ограниченным открытием реального типа целевого бина (только его реализованные интерфейсы).

Рекомендуемый способ узнать фактический тип времени выполнения конкретного бина - это вызов BeanFactory.getType для заданного имени бина. При этом учитываются все вышеперечисленные случаи и возвращается тип объекта, который будет возвращен вызовом BeanFactory.getBean для того же имени бина.