Сегодня мы рассмотрим жизненный цикл Spring Bean. Spring Beans являются самой важной частью любого приложения Spring. ApplicationContext Spring отвечает за инициализацию Spring Beans, определенных в файле конфигурации боба Spring.
Жизненный цикл Spring Bean
Контекст Spring также отвечает за внедрение зависимостей в боб, либо через методы установки, либо через конструкторные методы или с помощью автосвязывания Spring. Иногда мы хотим инициализировать ресурсы в классах бобов, например, создавать соединения с базой данных или проверять сторонние службы во время инициализации перед любым запросом клиента. Spring Framework предоставляет различные способы, с помощью которых мы можем предоставить методы после инициализации и перед уничтожением в жизненном цикле боба Spring.
- Реализуя интерфейсы InitializingBean и DisposableBean – Оба эти интерфейса объявляют единственный метод, где мы можем инициализировать/закрывать ресурсы в бине. Для пост-инициализации мы можем реализовать интерфейс
InitializingBean
и предоставить реализацию методаafterPropertiesSet()
. Для предварительного уничтожения мы можем реализовать интерфейсDisposableBean
и предоставить реализацию методаdestroy()
. Эти методы являются методами обратного вызова и аналогичны реализациям слушателя сервлетов. Этот подход прост в использовании, но не рекомендуется, потому что он создаст тесную связь с фреймворком Spring в наших реализациях бина. - Предоставление значений атрибутов init-method и destroy-method для бина в файле конфигурации бинов Spring. Этот подход рекомендуется из-за отсутствия прямой зависимости от фреймворка Spring и возможности создания собственных методов.
Обратите внимание, что как пост-инициализационные, так и предварительные методы уничтожения не должны иметь аргументов, но они могут генерировать исключения. Также нам потребуется получить экземпляр бина из контекста приложения Spring для вызова этих методов.
Жизненный цикл бина Spring – @PostConstruct, @PreDestroy Аннотации
Фреймворк Spring также поддерживает аннотации @PostConstruct
и @PreDestroy
для определения методов после инициализации и перед уничтожением. Эти аннотации являются частью пакета javax.annotation
. Однако для работы этих аннотаций необходимо сконфигурировать наше приложение Spring для поиска аннотаций. Мы можем сделать это либо, определив бин типа org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
, либо с помощью элемента context:annotation-config
в файле конфигурации бина Spring. Давайте напишем простое приложение Spring, чтобы продемонстрировать использование вышеуказанных конфигураций для управления жизненным циклом бина Spring. Создайте проект Spring Maven в Spring Tool Suite, итоговый проект будет выглядеть как на изображении ниже.
Жизненный цикл бина Spring – Зависимости Maven
Нам не нужно добавлять какие-либо дополнительные зависимости для конфигурации методов жизненного цикла бина Spring, наш файл pom.xml похож на любой другой стандартный проект Spring Maven.
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringBeanLifeCycle</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Жизненный цикл бина Spring – Класс модели
Давайте создадим простой класс Java Bean, который будет использоваться в сервисных классах.
package com.journaldev.spring.bean;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Жизненный цикл боба Spring – InitializingBean, DisposableBean
Давайте создадим сервисный класс, в котором мы реализуем оба интерфейса для методов пост-инициализации и предварительного уничтожения.
package com.journaldev.spring.service;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.journaldev.spring.bean.Employee;
public class EmployeeService implements InitializingBean, DisposableBean{
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public EmployeeService(){
System.out.println("EmployeeService no-args constructor called");
}
@Override
public void destroy() throws Exception {
System.out.println("EmployeeService Closing resources");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("EmployeeService initializing to dummy value");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
Жизненный цикл боба Spring – Пользовательские методы пост-инициализации, предварительного уничтожения
Поскольку мы не хотим, чтобы наши сервисы зависели от прямой зависимости от фреймворка Spring, давайте создадим другую форму класса Employee Service, где у нас будут методы жизненного цикла Spring для пост-инициализации и предварительного уничтожения, и мы настроим их в файле конфигурации боба Spring.
package com.journaldev.spring.service;
import com.journaldev.spring.bean.Employee;
public class MyEmployeeService{
private Employee employee;
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public MyEmployeeService(){
System.out.println("MyEmployeeService no-args constructor called");
}
//метод предварительного уничтожения
public void destroy() throws Exception {
System.out.println("MyEmployeeService Closing resources");
}
//метод пост-инициализации
public void init() throws Exception {
System.out.println("MyEmployeeService initializing to dummy value");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
Мы рассмотрим файл конфигурации боба Spring чуть позже. Перед этим давайте создадим еще один сервисный класс, который будет использовать аннотации @PostConstruct и @PreDestroy.
Жизненный цикл Spring Bean – @PostConstruct, @PreDestroy
Ниже приведен простой класс, который будет сконфигурирован как боб Spring, и для методов после инициализации и перед уничтожением мы используем аннотации @PostConstruct и @PreDestroy.
package com.journaldev.spring.service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyService {
@PostConstruct
public void init(){
System.out.println("MyService init method called");
}
public MyService(){
System.out.println("MyService no-args constructor called");
}
@PreDestroy
public void destory(){
System.out.println("MyService destroy method called");
}
}
Жизненный цикл Spring Bean – Файл конфигурации
Давайте посмотрим, как мы будем конфигурировать наши бобы в файле контекста Spring.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Not initializing employee name variable-->
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<bean name="myEmployeeService" class="com.journaldev.spring.service.MyEmployeeService"
init-method="init" destroy-method="destroy">
<property name="employee" ref="employee"></property>
</bean>
<!-- initializing CommonAnnotationBeanPostProcessor is same as context:annotation-config -->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean name="myService" class="com.journaldev.spring.service.MyService" />
</beans>
Обратите внимание, что я не инициализирую имя сотрудника в его определении боба. Поскольку EmployeeService использует интерфейсы, здесь нам не нужна никакая специальная конфигурация. Для боба MyEmployeeService мы используем атрибуты init-method и destroy-method, чтобы сообщить фреймворку Spring о наших пользовательских методах для выполнения. Конфигурация боба MyService не имеет ничего особенного, но, как видите, я включаю конфигурацию на основе аннотаций для этого. Наше приложение готово, давайте напишем тестовую программу, чтобы увидеть, как выполняются различные методы.
Жизненный цикл Spring Bean – Тестовая программа
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.service.EmployeeService;
import com.journaldev.spring.service.MyEmployeeService;
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
System.out.println("Spring Context initialized");
//MyEmployeeService service = ctx.getBean("myEmployeeService", MyEmployeeService.class);
EmployeeService service = ctx.getBean("employeeService", EmployeeService.class);
System.out.println("Bean retrieved from Spring Context");
System.out.println("Employee Name="+service.getEmployee().getName());
ctx.close();
System.out.println("Spring Context Closed");
}
}
При запуске вышеуказанной тестовой программы мы получаем следующий вывод.
Apr 01, 2014 10:50:50 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: startup date [Tue Apr 01 22:50:50 PDT 2014]; root of context hierarchy
Apr 01, 2014 10:50:50 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
EmployeeService no-args constructor called
EmployeeService initializing to dummy value
MyEmployeeService no-args constructor called
MyEmployeeService initializing to dummy value
MyService no-args constructor called
MyService init method called
Spring Context initialized
Bean retrieved from Spring Context
Employee Name=Pankaj
Apr 01, 2014 10:50:50 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@c1b9b03: startup date [Tue Apr 01 22:50:50 PDT 2014]; root of context hierarchy
MyService destroy method called
MyEmployeeService Closing resources
EmployeeService Closing resources
Spring Context Closed
Важные моменты жизненного цикла Spring Bean:
- Из вывода на консоль ясно, что Spring Context сначала использует конструктор без аргументов для инициализации объекта бина, а затем вызывает метод post-init.
- Порядок инициализации бинов такой же, как определено в конфигурационном файле бинов Spring.
- Контекст возвращается только тогда, когда все бины Spring правильно инициализированы с выполнением методов post-init.
- Имя сотрудника выводится как “Pankaj”, потому что оно было инициализировано в методе post-init.
- При закрытии контекста бины уничтожаются в обратном порядке, в котором они были инициализированы, то есть в порядке LIFO (Last-In-First-Out).
Вы можете раскомментировать код, чтобы получить бин типа MyEmployeeService
и подтвердить, что вывод будет аналогичен и следует всем вышеупомянутым моментам.
Интерфейсы Spring Aware
Иногда нам нужны объекты Spring Framework в наших бинах, чтобы выполнять некоторые операции, например, чтение параметров ServletConfig и ServletContext или знать определения бинов, загруженных ApplicationContext. Вот почему Spring Framework предоставляет целую группу интерфейсов *Aware, которые мы можем реализовать в классах наших бинов. `org.springframework.beans.factory.Aware` является корневым маркерным интерфейсом для всех этих интерфейсов Aware. Все интерфейсы *Aware являются подинтерфейсами Aware и объявляют единственный метод setter, который должен быть реализован бином. Затем контекст Spring использует инъекцию зависимостей на основе setter’ов для внедрения соответствующих объектов в бин и делает их доступными для нашего использования. Интерфейсы Spring Aware аналогичны слушателям сервлетов с методами обратного вызова и реализацией паттерна проектирования наблюдателя. Некоторые из важных интерфейсов Aware:
- ApplicationContextAware – для внедрения объекта ApplicationContext, пример использования – получение массива имен определений бинов.
- BeanFactoryAware – для внедрения объекта BeanFactory, пример использования – проверка области видимости бина.
- BeanNameAware – для получения имени бина, определенного в конфигурационном файле.
- ResourceLoaderAware – для внедрения объекта ResourceLoader, пример использования – получение входного потока для файла в classpath.
- ServletContextAware – для внедрения объекта ServletContext в приложение MVC, пример использования – чтение параметров контекста и атрибутов.
- ServletConfigAware – для внедрения объекта ServletConfig в приложение MVC, пример использования – получение параметров конфигурации сервлета.
Давайте рассмотрим использование этих интерфейсов Aware в действии, реализуя несколько из них в классе, который мы будем конфигурировать как бин Spring.
package com.journaldev.spring.service;
import java.util.Arrays;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
public class MyAwareService implements ApplicationContextAware,
ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware {
@Override
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
System.out.println("setApplicationContext called");
System.out.println("setApplicationContext:: Bean Definition Names="
+ Arrays.toString(ctx.getBeanDefinitionNames()));
}
@Override
public void setBeanName(String beanName) {
System.out.println("setBeanName called");
System.out.println("setBeanName:: Bean Name defined in context="
+ beanName);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("setBeanClassLoader called");
System.out.println("setBeanClassLoader:: ClassLoader Name="
+ classLoader.getClass().getName());
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
System.out.println("setResourceLoader called");
Resource resource = resourceLoader.getResource("classpath:spring.xml");
System.out.println("setResourceLoader:: Resource File Name="
+ resource.getFilename());
}
@Override
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
System.out.println("setImportMetadata called");
}
@Override
public void setEnvironment(Environment env) {
System.out.println("setEnvironment called");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("setBeanFactory called");
System.out.println("setBeanFactory:: employee bean singleton="
+ beanFactory.isSingleton("employee"));
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
System.out.println("setApplicationEventPublisher called");
}
}
Пример файла конфигурации Spring *Aware
Очень простой файл конфигурации бина Spring.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="myAwareService" class="com.journaldev.spring.service.MyAwareService" />
</beans>
Программа тестирования Spring *Aware
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.service.MyAwareService;
public class SpringAwareMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aware.xml");
ctx.getBean("myAwareService", MyAwareService.class);
ctx.close();
}
}
Теперь, когда мы запускаем вышеуказанный класс, мы получаем следующий вывод.
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: startup date [Tue Apr 01 23:27:05 PDT 2014]; root of context hierarchy
Apr 01, 2014 11:27:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-aware.xml]
setBeanName called
setBeanName:: Bean Name defined in context=myAwareService
setBeanClassLoader called
setBeanClassLoader:: ClassLoader Name=sun.misc.Launcher$AppClassLoader
setBeanFactory called
setBeanFactory:: employee bean singleton=true
setEnvironment called
setResourceLoader called
setResourceLoader:: Resource File Name=spring.xml
setApplicationEventPublisher called
setApplicationContext called
setApplicationContext:: Bean Definition Names=[employee, myAwareService]
Apr 01, 2014 11:27:05 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@60a2f435: startup date [Tue Apr 01 23:27:05 PDT 2014]; root of context hierarchy
Вывод консоли тестовой программы прост в понимании, я не буду углубляться в много деталей. Вот и все для методов жизненного цикла бина Spring и внедрения объектов, специфичных для фреймворка, в бины Spring. Пожалуйста, загрузите образец проекта по следующей ссылке и проанализируйте его, чтобы узнать больше о них.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-life-cycle