Cycle de Vie des Beans Spring

Aujourd’hui, nous examinerons le cycle de vie des beans Spring. Les beans Spring sont la partie la plus importante de toute application Spring. Le ApplicationContext Spring est responsable de l’initialisation des beans Spring définis dans le fichier de configuration des beans Spring.

Cycle de vie des beans Spring

Le contexte Spring est également responsable de l’injection de dépendances dans le bean, que ce soit par des méthodes setter ou constructeur ou par l’autocâblage Spring. Parfois, nous voulons initialiser des ressources dans les classes de bean, par exemple créer des connexions de base de données ou valider des services tiers au moment de l’initialisation avant toute demande client. Le framework Spring propose différentes façons de fournir des méthodes de post-initialisation et de pré-destruction dans le cycle de vie d’un bean Spring.

  1. En implémentant les interfaces InitializingBean et DisposableBean – Ces deux interfaces déclarent une méthode unique où nous pouvons initialiser/fermer les ressources dans le bean. Pour la post-initialisation, nous pouvons implémenter l’interface InitializingBean et fournir une implémentation de la méthode afterPropertiesSet(). Pour la pré-destruction, nous pouvons implémenter l’interface DisposableBean et fournir une implémentation de la méthode destroy(). Ces méthodes sont des méthodes de rappel et similaires aux implémentations de listener de servlet. Cette approche est simple à utiliser mais elle n’est pas recommandée car elle créera un couplage étroit avec le framework Spring dans nos implémentations de bean.
  2. En fournissant les valeurs d’attribut init-method et destroy-method pour le bean dans le fichier de configuration du bean Spring. C’est l’approche recommandée en raison de l’absence de dépendance directe au framework Spring et nous pouvons créer nos propres méthodes.

Notez que les méthodes post-init et pre-destroy doivent toutes deux n’avoir aucun argument mais elles peuvent générer des exceptions. Nous aurions également besoin d’obtenir l’instance du bean du contexte d’application Spring pour l’invocation de ces méthodes.

Cycle de vie du bean Spring – @PostConstruct, @PreDestroy Annotations

Le framework Spring prend également en charge les annotations @PostConstruct et @PreDestroy pour définir les méthodes post-init et pre-destroy. Ces annotations font partie du package javax.annotation. Cependant, pour que ces annotations fonctionnent, nous devons configurer notre application Spring pour rechercher les annotations. Nous pouvons le faire soit en définissant un bean de type org.springframework.context.annotation.CommonAnnotationBeanPostProcessor, soit en utilisant l’élément context:annotation-config dans le fichier de configuration des beans Spring. Écrivons une application Spring simple pour montrer l’utilisation des configurations ci-dessus pour la gestion du cycle de vie des beans Spring. Créez un projet Maven Spring dans Spring Tool Suite, le projet final ressemblera à l’image ci-dessous.

Cycle de vie des beans Spring – Dépendances Maven

Nous n’avons pas besoin d’inclure de dépendances supplémentaires pour configurer les méthodes de cycle de vie des beans Spring, notre fichier pom.xml est comme celui de n’importe quel autre projet Maven Spring standard.

<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>

Cycle de vie des beans Spring – Classe de modèle

Créons une classe de bean Java simple qui sera utilisée dans les classes de service.

package com.journaldev.spring.bean;

public class Employee {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

Cycle de vie du Bean Spring – InitializingBean, DisposableBean

Créons une classe de service où nous implémenterons les deux interfaces pour les méthodes post-init et 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");
		}
	}
}

Cycle de vie du Bean Spring – post-init personnalisé, pre-destroy

Puisque nous ne voulons pas que nos services dépendent directement du framework Spring, créons une autre forme de classe de service Employee où nous aurons des méthodes de cycle de vie Spring post-init et pre-destroy que nous configurerons dans le fichier de configuration du 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");
	}

	// méthode pre-destroy
	public void destroy() throws Exception {
		System.out.println("MyEmployeeService Closing resources");
	}

	// méthode post-init
	public void init() throws Exception {
		System.out.println("MyEmployeeService initializing to dummy value");
		if(employee.getName() == null){
			employee.setName("Pankaj");
		}
	}
}

Nous examinerons le fichier de configuration du bean Spring dans un instant. Avant cela, créons une autre classe de service qui utilisera les annotations @PostConstruct et @PreDestroy.

Cycle de vie du Bean Spring – @PostConstruct, @PreDestroy

Voici une classe simple qui sera configurée en tant que bean Spring, et pour les méthodes post-init et pre-destroy, nous utilisons les annotations @PostConstruct et @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");
	}
}

Cycle de vie du Bean Spring – Fichier de configuration

Voyons comment nous allons configurer nos beans dans le fichier de contexte 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>

Remarquez que je n’initialise pas le nom de l’employé dans sa définition de bean. Étant donné que EmployeeService utilise des interfaces, nous n’avons pas besoin d’une configuration spéciale ici. Pour le bean MyEmployeeService, nous utilisons les attributs init-method et destroy-method pour indiquer au framework Spring les méthodes personnalisées à exécuter. La configuration du bean MyService n’a rien de spécial, mais comme vous pouvez le voir, j’active la configuration basée sur les annotations pour celui-ci. Notre application est prête, écrivons un programme de test pour voir comment différentes méthodes sont exécutées.

Cycle de vie du Bean Spring – Programme de test

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");
	}

}

Lorsque nous exécutons le programme de test ci-dessus, nous obtenons la sortie suivante.

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

Points importants du cycle de vie du Bean Spring:

  • À partir de la sortie de la console, il est clair que le contexte Spring utilise d’abord le constructeur sans argument pour initialiser l’objet bean, puis appelle la méthode post-init.
  • L’ordre d’initialisation des beans est le même que celui défini dans le fichier de configuration des beans Spring.
  • Le contexte n’est renvoyé que lorsque tous les beans Spring sont correctement initialisés avec les exécutions de la méthode post-init.
  • Le nom de l’employé est imprimé comme « Pankaj » car il a été initialisé dans la méthode post-init.
  • Lorsque le contexte se ferme, les beans sont détruits dans l’ordre inverse de leur initialisation, c’est-à-dire dans l’ordre LIFO (Last-In-First-Out).

Vous pouvez décommenter le code pour obtenir un bean de type MyEmployeeService et confirmer que la sortie sera similaire et suivra tous les points mentionnés ci-dessus.

Interfaces Spring Aware

Parfois, nous avons besoin d’objets Spring Framework dans nos beans pour effectuer certaines opérations, par exemple, lire les paramètres ServletConfig et ServletContext ou connaître les définitions de bean chargées par l’ApplicationContext. C’est pourquoi le framework Spring fournit un ensemble d’interfaces *Aware que nous pouvons implémenter dans nos classes de bean. org.springframework.beans.factory.Aware est l’interface de marquage racine pour toutes ces interfaces *Aware. Toutes les interfaces *Aware sont des sous-interfaces de Aware et déclarent une seule méthode setter à implémenter par le bean. Ensuite, le contexte Spring utilise l’injection de dépendance basée sur les setters pour injecter les objets correspondants dans le bean et les rendre disponibles pour notre utilisation. Les interfaces Aware de Spring sont similaires aux écouteurs de servlets avec des méthodes de rappel et implémentent le patron de conception observer. Certaines des interfaces Aware importantes sont :

  • ApplicationContextAware – pour injecter l’objet ApplicationContext, l’utilisation exemple est d’obtenir le tableau des noms de définition de bean.
  • BeanFactoryAware – pour injecter l’objet BeanFactory, l’utilisation exemple est de vérifier la portée d’un bean.
  • BeanNameAware – pour connaître le nom du bean défini dans le fichier de configuration.
  • ResourceLoaderAware – pour injecter l’objet ResourceLoader, l’exemple d’utilisation consiste à obtenir le flux d’entrée pour un fichier dans le classpath.
  • ServletContextAware – pour injecter l’objet ServletContext dans une application MVC, l’exemple d’utilisation consiste à lire les paramètres et attributs du contexte.
  • ServletConfigAware – pour injecter l’objet ServletConfig dans une application MVC, l’exemple d’utilisation consiste à obtenir les paramètres de configuration du servlet.

Observons l’utilisation de ces interfaces Aware en action en les implémentant dans une classe que nous configurerons comme bean 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");
	}

}

Exemple de fichier de configuration Spring *Aware

Fichier de configuration de bean Spring très simple.

<?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>

Programme de test 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();
	}

}

Maintenant, lorsque nous exécutons la classe ci-dessus, nous obtenons la sortie suivante.

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

La sortie de la console du programme de test est simple à comprendre, je n’entrerai pas dans les détails. C’est tout pour les méthodes du cycle de vie des beans Spring et l’injection d’objets spécifiques au framework dans les beans Spring. Veuillez télécharger le projet d’exemple à partir du lien ci-dessous et l’analyser pour en savoir plus à leur sujet.

Télécharger le projet Spring Bean Lifecycle

Source:
https://www.digitalocean.com/community/tutorials/spring-bean-life-cycle