Визначення біна — це, власне, набір правил для створення одного чи кількох об'єктів. У відповідь на запит контейнер переглядає набір правил іменованого біна і використовує метадані конфігурації, що містяться у визначенні цього біна, для створення (або отримання) фактичного об'єкта.

Якщо ти використовуєш конфігураційні метадані на основі 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 використовується для конкретного біна, може знадобитися конструктор за замовчуванням (порожній). Він не обмежується керуванням справжніми 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 для того ж імені біна.