Опишите жизненный цикл Spring Bean
Beans – центральный объект заботы Spring Framework. За кулисами фреймворка с ними происходит множество процессов. Во многие из них можно вмешаться, добавив собственную логику в разные этапы жизненного цикла. Через следующие этапы проходит каждый отдельно взятый бин:
1. Инстанцирование объекта. Техническое начало жизни бина, работа конструктора его класса;
2. Установка свойств из конфигурации бина, внедрение зависимостей;
3. Нотификация aware-интерфейсов. BeanNameAware , BeanFactoryAware и другие. Мы уже писали о таких интерфейсах ранее. Технически, выполняется системными подтипами BeanPostProcessor , и совпадает с шагом 4;
4. Пре-инициализация – метод postProcessBeforeInitialization() интерфейса BeanPostProcessor ;
5. Инициализация. Разные способы применяются в таком порядке:
• Метод бина с аннотацией @PostConstruct из стандарта JSR-250 (рекомендуемый способ);
• Метод afterPropertiesSet() бина под интерфейсом InitializingBean ;
• Init-метод. Для отдельного бина его имя устанавливается в параметре определения initMethod . В xml-конфигурации можно установить для всех бинов сразу, с помощью default-init-method ;
6. Пост-инициализация – метод postProcessAfterInitialization() интерфейса BeanPostProcessor .
Когда IoC-контейнер завершает свою работу, мы можем кастомизировать этап штатного уничтожения бина. Как со всеми способами финализации в Java, при жестком выключении ( kill -9 ) гарантии вызова этого этапа нет. Три альтернативных способа «деинициализации» вызываются в том же порядке, что симметричные им методы инициализации:
1. Метод с аннотацией @PreDestroy ;
2. Метод с именем, которое указано в свойстве destroyMethod определния бина (или в глобальном default-destroy-method );
3. Метод destroy() интерфейса DisposableBean .
Не следует путать жизненный цикл отдельного бина с жизненным циклом контекста и этапами подготовки фабрик бинов. О них мы поговорим в будущих публикациях.
Как настроить класс как Spring Bean?
Термин «бин» используется в Spring для ссылки на любой компонент, который управляется контейнером. То есть бины – это объекты, являющиеся основой приложения и управляемые Spring IoC-контейнером. Как правило, на определенном уровне бины придерживаются спецификации JavaBean, однако это необязательно, особенно когда для связывания бинов друг с другом разработчик планирует использовать Constructor Injection.
Чтобы получить экземпляр бина, применяют ApplicationContext. При этом IoC-контейнер управляет жизненным циклом бина, а также областью видимости и внедрением.
Если говорить о конфигурационном файле Spring Bean, то он имеет особое значение. В частности, он определяет все бины, которые будут инициализированы в Spring Context. При этом в процессе создания экземпляра Spring ApplicationContext конфигурационный xml-файл будет прочитан, следовательно, будут выполнены необходимые инициализации, указанные в этом файле. Также в этом файле, отдельно от базовой конфигурации, могут быть настройки локализации, описания перехватчиков (interceptors) и прочее.
Итак, существует ряд способов работы с классами в Spring Framework:
- XML-конфигурация,
- Java based-конфигурация
- конфигурация Annotation based.
Давайте рассмотрим их чуть подробнее.
XML-конфигурация
Java based-конфигурация
В данном случае все настройки и указания бинов прописывают в Java-коде:

При этом для извлечения бина из контекста применяют следующий подход:

Annotation based-конфигурация
Ее можно использовать внутри кода аннотации @Repository, @Component, @Service, @Controller, что делается для указания классов в качестве Spring-бинов. А для их поиска и управления контейнером в xml-файл прописывают следующую настройку:
Что такое Spring Bean
IoC-контейнер Spring управляет одним или несколькими бинами. Эти бины создаются с помощью конфигурационных метаданных, которые вы предоставляете контейнеру (например, в виде XML -определений ).
В самом контейнере эти определения бинов представлены в виде объектов BeanDefinition , которые содержат (среди прочей информации) следующие метаданные:
- Полное имя класса с указанием пакета: как правило, это фактический класс реализации определяемого бина.
- Элементы поведенческой конфигурации бина, которые определяют то, как бин должен вести себя в контейнере (область действия, обратные вызовы жизненного цикла и так далее).
- Ссылки на другие бины, которые необходимы для его работы. Эти ссылки также называются взаимодействующими объектами (collaborators) или зависимостями (dependencies).
- Другие параметры конфигурации, которые необходимо установить во вновь созданном объекте, например, предельный размер пула или количество соединений, используемых в бине, который управляет пулом соединений.
Эти метаданные преобразуются в набор свойств, которые составляют определение каждого бина. В следующей таблице описаны эти свойства:
Режим автоматического обнаружения и связывания
Режим отложенной инициализации
В дополнение к определениям бинов, которые содержат информацию о том, как создать конкретный бин, реализации ApplicationContext также позволяют регистрировать существующие объекты, которые создаются вне контейнера (пользователями). Это возможно осуществить путем обращения к BeanFactory ApplicationContext через метод getBeanFactory() , который возвращает реализацию DefaultListableBeanFactory . DefaultListableBeanFactory поддерживает данную регистрацию через методы registerSingleton(..) и registerBeanDefinition(..) . Однако типичные приложения работают исключительно с бинами, определенными с помощью стандартных метаданных определения бинов.
Метаданные бинов и экземпляры-одиночки, предоставляемые вручную, должны быть зарегистрированы как можно скорее, чтобы контейнер мог правильно обработать их во время автоматического обнаружения и связывания и других этапов интроспекции. Хотя переопределение существующих метаданных и существующих экземпляров-одиночек и поддерживается в некоторой степени, регистрация новых бинов во время выполнения (одновременно с прямым доступом к фабрике) официально не поддерживается и может привести к исключениям одновременного доступа, несовместимому состоянию в контейнере бинов или к обоим этим исключениям.
Именование бинов
Каждый бин имеет один или несколько идентификаторов. Эти идентификаторы должны быть уникальными в пределах контейнера, в котором находится бин. Бины обычно имеют только один идентификатор. Однако, если требуется более одного, дополнительные можно считать псевдонимами.
В конфигурационных метаданных на основе XML используется атрибут id , атрибут name или оба для задания идентификаторов бина. Атрибут id позволяет задать только один идентификатор. Обычно эти имена являются буквенно-цифровыми (‘myBean’, ‘someService’ и т.д.), но они могут содержать и специальные символы. Если вы хотите ввести другие псевдонимы для бина, то также можете задать их в атрибуте name , отделив запятой ( , ), точкой с запятой ( ; ) или пробелом. Историческая справка: в версиях до Spring 3.1 атрибут id был определен как тип xsd:ID , что ограничивало применение символов. Начиная с версии 3.1, он определяется как тип xsd:string . Обратите внимание, что уникальность id бина все еще обеспечивается контейнером, но уже не анализаторами XML.
Не нужно указывать name или id для бина. Если явно не задать name или id , контейнер сгенерирует уникальное имя для этого бина. Однако если вы хотите ссылаться на этот бин по имени, используя элемент ref или поиск в стиле локатора служб (Service Locator), то должны указать имя. Мотивы отказа от указания имени связаны с использованием внутренних бинов и взаимодействующих объектов с автоматическим обнаружением и связыванием.
Соглашения об именовании бинов
Соглашение заключается в использовании типового соглашения Java для именования полей экземпляра при именовании бинов. То есть имена бинов начинаются со строчной буквы и продолжаются camelCase (верблюжьем) регистре. Примерами таких имен являются accountManager , accountService , userDao , loginController и так далее.
Последовательное именование бинов делает конфигурацию более легкой для чтения и понимания. Кроме того, если вы пользуетесь АОП в Spring, то это очень помогает при применении советов (advice) к набору бинов, связанных по имени.
С помощью сканирования компонентов по пути класса (classpath), Spring генерирует имена бинов для непоименованных компонентов, следуя правилам, описанным ранее: по сути, берется простое имя класса, а его начальный символ переводится в нижний регистр. Однако в (необычном) особом случае, когда имеется более одного символа, а первый и второй символы находятся в верхнем регистре, исходный регистр сохраняется. Это те же правила, которые определены в java.beans.Introspector.decapitalize (который Spring использует здесь).
Присвоение псевдонима бину вне определения бина
В самом определении бина можно указать более одного имени для бина, используя комбинацию из одного имени, указанного атрибутом id , и любого количества других имен в атрибуте name . Эти имена могут быть эквивалентными псевдонимами одного и того же бина и полезны в некоторых ситуациях, например, когда каждый компонент в приложении ссылается на общую зависимость, используя имя бина, специфичное для этого компонента.
Однако задание всех псевдонимов, в которых фактических определен бин, не всегда является достаточным. Иногда желательно ввести псевдоним для бина, который определен в другом месте. Это обычно имеет место в больших системах, где конфигурация разделена между каждой подсистемой, а каждая подсистема имеет свой собственный набор определений объектов. В конфигурационных метаданных на основе XML для этого можно использовать элемент . В следующем примере показано, как это сделать:
В этом случае бин (в том же контейнере) с именем fromName — после использования этого определения псевдонима — может упоминаться как toName .
Например, конфигурационные метаданные для подсистемы A могут ссылаться на источник данных DataSource под именем subsystemA-dataSource . Конфигурационные метаданные для подсистемы B могут ссылаться на источник данных DataSource под именем subsystemB-dataSource . При создании основного приложения, использующего обе эти подсистемы, основное приложение обращается к источнику данных DataSource под именем myApp-dataSource . Чтобы все три имени ссылались на один и тот же объект, вы можете добавить следующие определения псевдонимов в конфигурационные метаданные:
Теперь каждый компонент и основное приложение могут обращаться к dataSource через имя, которое является уникальным и гарантированно не будет конфликтовать ни с каким другим определением (эффективно создавая пространство имен), но при этом они обращаются к одному и тому же бину.
Java-конфигурация
Если вы используете Javaconfiguration, аннотация @Bean может быть использована для предоставления псевдонимов.
Basic Concepts: @Bean and @Configuration
The central artifacts in Spring’s Java configuration support are @Configuration -annotated classes and @Bean -annotated methods.
The @Bean annotation is used to indicate that a method instantiates, configures, and initializes a new object to be managed by the Spring IoC container. For those familiar with Spring’s XML configuration, the @Bean annotation plays the same role as the element. You can use @Bean -annotated methods with any Spring @Component . However, they are most often used with @Configuration beans.
Annotating a class with @Configuration indicates that its primary purpose is as a source of bean definitions. Furthermore, @Configuration classes let inter-bean dependencies be defined by calling other @Bean methods in the same class. The simplest possible @Configuration class reads as follows:
@Configuration public class AppConfig < @Bean public MyServiceImpl myService() < return new MyServiceImpl(); >>
@Configuration class AppConfig < @Bean fun myService(): MyServiceImpl < return MyServiceImpl() >>
The preceding AppConfig class is equivalent to the following Spring XML:
Full @Configuration vs “lite” @Bean mode?
When @Bean methods are declared within classes that are not annotated with @Configuration , they are referred to as being processed in a “lite” mode. Bean methods declared on a bean that is not annotated with @Configuration are considered to be “lite”, with a different primary purpose of the containing class and a @Bean method being a sort of bonus there. For example, service components may expose management views to the container through an additional @Bean method on each applicable component class. In such scenarios, @Bean methods are a general-purpose factory method mechanism.
Unlike full @Configuration , lite @Bean methods cannot declare inter-bean dependencies. Instead, they operate on their containing component’s internal state and, optionally, on arguments that they may declare. Such a @Bean method should therefore not invoke other @Bean methods. Each such method is literally only a factory method for a particular bean reference, without any special runtime semantics. The positive side-effect here is that no CGLIB subclassing has to be applied at runtime, so there are no limitations in terms of class design (that is, the containing class may be final and so forth).
In common scenarios, @Bean methods are to be declared within @Configuration classes, ensuring that “full” mode is always used and that cross-method references therefore get redirected to the container’s lifecycle management. This prevents the same @Bean method from accidentally being invoked through a regular Java call, which helps to reduce subtle bugs that can be hard to track down when operating in “lite” mode.
The @Bean and @Configuration annotations are discussed in depth in the following sections. First, however, we cover the various ways of creating a spring container by using Java-based configuration.
