Весенний жизненный цикл бобов
Сегодня мы рассмотрим жизненный цикл Spring Bean. Spring Beans — самая важная часть любого приложения Spring. Spring ApplicationContext отвечает за инициализацию Spring Beans, определенных в файле конфигурации Spring Bean.
Весенний жизненный цикл бобов
- Путем реализации интерфейсов InitializingBean и DisposableBean. Оба этих интерфейса объявляют единый метод, с помощью которого мы можем инициализировать/закрывать ресурсы в компоненте. Для пост-инициализации мы можем реализовать интерфейс
InitializingBean
и обеспечить реализацию методаafterPropertiesSet()
. Для предварительного уничтожения мы можем реализовать интерфейсDisposableBean
и обеспечить реализацию методаdestroy()
. Эти методы являются методами обратного вызова и аналогичны реализациям прослушивателя сервлета. Этот подход прост в использовании, но не рекомендуется, поскольку он создаст тесную связь с инфраструктурой Spring в наших реализациях bean-компонентов. - Предоставление значений атрибутов init-method и destroy-method для компонента в файле конфигурации компонента spring. Это рекомендуемый подход из-за отсутствия прямой зависимости от среды Spring, и мы можем создавать собственные методы.
Обратите внимание, что оба метода post-init и pre-destroy не должны иметь аргументов, но могут генерировать исключения. Нам также потребуется получить экземпляр компонента из контекста приложения Spring для вызова этих методов.
Жизненный цикл Spring Bean — аннотации @PostConstruct, @PreDestroy
Жизненный цикл Spring Bean — зависимости Maven
Нам не нужно включать какие-либо дополнительные зависимости для настройки методов жизненного цикла Spring bean, наш файл 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 Bean — класс модели
Давайте создадим простой класс 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 Bean — InitializingBean, DisposableBean
Давайте создадим сервисный класс, в котором мы будем реализовывать интерфейсы для методов post-init и pre-destroy.
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 Bean — пользовательский пост-инициализацию, предварительное уничтожение
Поскольку мы не хотим, чтобы наши сервисы имели прямую зависимость от Spring Framework, давайте создадим другую форму класса Employee Service, в которой у нас будут методы жизненного цикла Spring после инициализации и перед уничтожением, и мы настроим их в файле конфигурации bean-компонента 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");
}
//pre-destroy method
public void destroy() throws Exception {
System.out.println("MyEmployeeService Closing resources");
}
//post-init method
public void init() throws Exception {
System.out.println("MyEmployeeService initializing to dummy value");
if(employee.getName() == null){
employee.setName("Pankaj");
}
}
}
Чуть позже мы рассмотрим файл конфигурации bean-компонента Spring. Перед этим давайте создадим еще один сервисный класс, который будет использовать аннотации @PreDestroy.
Жизненный цикл Spring Bean — @PostConstruct, @PreDestroy
Ниже приведен простой класс, который будет настроен как bean-компонент Spring, а для методов post-init и pre-destroy мы используем аннотации @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 — файл конфигурации
Давайте посмотрим, как мы будем настраивать наши 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>
Обратите внимание, что я не инициализирую имя сотрудника в определении bean-компонента. Поскольку EmployeeService использует интерфейсы, здесь нам не нужна никакая специальная конфигурация. Для bean-компонента MyEmployeeService мы используем атрибуты init-method и destroy-method, чтобы позволить Spring Framework знать наши пользовательские методы для выполнения. Конфигурация bean-компонента 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.
- Порядок инициализации bean-компонента такой же, как он определен в файле конфигурации bean-компонента spring.
- Контекст возвращается только в том случае, если все компоненты Spring правильно инициализированы с выполнением методов после инициализации.
- Имя сотрудника печатается как \Pankaj, поскольку оно было инициализировано в методе post-init.
- Когда контекст закрывается, bean-компоненты уничтожаются в порядке, обратном тому, в котором они были инициализированы, т. е. в порядке LIFO (последним пришел — первым вышел).
Вы можете раскомментировать код, чтобы получить bean-компонент типа MyEmployeeService
, и подтвердить, что выходные данные будут аналогичными и будут следовать всем пунктам, упомянутым выше.
Интерфейсы с поддержкой Spring
Иногда нам нужны объекты Spring Framework в наших bean-компонентах для выполнения некоторых операций, например чтения параметров ServletConfig и ServletContext или для определения определений bean-компонентов, загруженных ApplicationContext. Вот почему Spring Framework предоставляет набор интерфейсов *Aware, которые мы можем реализовать в наших классах bean-компонентов. org.springframework.beans.factory.Aware
— это интерфейс корневого маркера для всех этих интерфейсов Aware. Все интерфейсы *Aware являются подинтерфейсами Aware и объявляют единственный метод установки, который должен быть реализован компонентом. Затем контекст Spring использует инъекцию зависимостей на основе установки для внедрения соответствующих объектов в bean-компонент и делает его доступным для нашего использования. Интерфейсы Spring Aware аналогичны шаблону проектирования Observer. Некоторые из важных интерфейсов Aware:
- ApplicationContextAware — для внедрения объекта ApplicationContext, например, для получения массива имен определений bean-компонентов.
- BeanFactoryAware — для внедрения объекта BeanFactory, пример использования — проверка области действия bean-компонента.
- BeanNameAware — чтобы узнать имя компонента, определенное в файле конфигурации.
- ResourceLoaderAware — для внедрения объекта ResourceLoader, например, для получения входного потока для файла в пути к классам.
- ServletContextAware — для внедрения объекта ServletContext в приложение MVC, пример использования — чтение параметров и атрибутов контекста.
- ServletConfigAware — для внедрения объекта ServletConfig в приложение MVC, например, для получения параметров конфигурации сервлета.
Давайте посмотрим на использование этих интерфейсов Aware в действии, реализовав несколько из них в классе, который мы настроим как Spring Bean.
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 Пример файла конфигурации
Очень простой файл конфигурации 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">
<bean name="employee" class="com.journaldev.spring.bean.Employee" />
<bean name="myAwareService" class="com.journaldev.spring.service.MyAwareService" />
</beans>
Spring *Aware Test Program
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 Bean и внедрения конкретных объектов инфраструктуры в Spring Bean. Загрузите образец проекта по ссылке ниже и проанализируйте его, чтобы узнать о них больше.
Скачать проект Spring Bean Lifecycle