Поиск по сайту:

Весенний жизненный цикл бобов


Сегодня мы рассмотрим жизненный цикл Spring Bean. Spring Beans — самая важная часть любого приложения Spring. Spring ApplicationContext отвечает за инициализацию Spring Beans, определенных в файле конфигурации Spring Bean.

Весенний жизненный цикл бобов

  1. Путем реализации интерфейсов InitializingBean и DisposableBean. Оба этих интерфейса объявляют единый метод, с помощью которого мы можем инициализировать/закрывать ресурсы в компоненте. Для пост-инициализации мы можем реализовать интерфейс InitializingBean и обеспечить реализацию метода afterPropertiesSet(). Для предварительного уничтожения мы можем реализовать интерфейс DisposableBean и обеспечить реализацию метода destroy(). Эти методы являются методами обратного вызова и аналогичны реализациям прослушивателя сервлета. Этот подход прост в использовании, но не рекомендуется, поскольку он создаст тесную связь с инфраструктурой Spring в наших реализациях bean-компонентов.
  2. Предоставление значений атрибутов 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