Ciclo di vita di Spring Bean

Oggi esamineremo il Ciclo di Vita del Bean di Spring. I Fagioli di Primavera sono la parte più importante di qualsiasi applicazione Spring. Il ApplicationContext di Spring è responsabile dell’inizializzazione dei Fagioli di Primavera definiti nel file di configurazione del fagiolo di primavera.

Ciclo di Vita del Bean di Spring

Il Contesto di Primavera è anche responsabile dell’iniezione delle dipendenze nel fagiolo, sia attraverso i metodi setter o costruttore o tramite autowiring di spring. A volte vogliamo inizializzare risorse nelle classi dei fagioli, ad esempio creare connessioni al database o convalidare servizi di terze parti al momento dell’inizializzazione prima di qualsiasi richiesta del client. Il framework Spring fornisce diversi modi attraverso i quali possiamo fornire metodi di post-inizializzazione e pre-distruzione in un ciclo di vita del fagiolo di primavera.

  1. Implementando le interfacce InitializingBean e DisposableBean – Entrambe queste interfacce dichiarano un singolo metodo in cui possiamo inizializzare/chiudere le risorse nel bean. Per la post-inizializzazione, possiamo implementare l’interfaccia InitializingBean e fornire l’implementazione del metodo afterPropertiesSet(). Per la pre-distruzione, possiamo implementare l’interfaccia DisposableBean e fornire l’implementazione del metodo destroy(). Questi metodi sono i metodi di callback e simili alle implementazioni dei listener del servlet. Questo approccio è semplice da usare ma non è consigliato perché creerà un accoppiamento stretto con il framework Spring nelle nostre implementazioni di bean.
  2. Fornire i valori degli attributi init-method e destroy-method per il bean nel file di configurazione del bean Spring. Questo è l’approccio consigliato a causa dell’assenza di dipendenze dirette dal framework Spring e possiamo creare i nostri metodi.

Si noti che sia i metodi post-init che pre-destroy dovrebbero non avere argomenti ma possono lanciare eccezioni. Sarà anche necessario ottenere l’istanza del bean dal contesto dell’applicazione Spring per l’invocazione di questi metodi.

Ciclo di vita del bean Spring – @PostConstruct, @PreDestroy Annotations

Il framework Spring supporta anche le annotazioni @PostConstruct e @PreDestroy per definire i metodi di post-inizializzazione e pre-destroy. Queste annotazioni fanno parte del pacchetto javax.annotation. Tuttavia, affinché queste annotazioni funzionino, è necessario configurare l’applicazione Spring per cercare le annotazioni. Possiamo farlo definendo un bean di tipo org.springframework.context.annotation.CommonAnnotationBeanPostProcessor o utilizzando l’elemento context:annotation-config nel file di configurazione dei bean di Spring. Scriviamo un’applicazione Spring semplice per mostrare l’utilizzo delle configurazioni sopra descritte per la gestione del ciclo di vita dei bean di Spring. Crea un progetto Maven Spring in Spring Tool Suite, il progetto finale avrà l’aspetto dell’immagine sottostante.

Ciclo di vita del bean di Spring – Dipendenze Maven

Non è necessario includere ulteriori dipendenze per configurare i metodi del ciclo di vita del bean di Spring, il nostro file pom.xml è come quello di qualsiasi altro progetto 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>

Ciclo di vita del bean di Spring – Classe Modello

Creiamo una semplice classe Java Bean che verrà utilizzata nelle classi di servizio.

package com.journaldev.spring.bean;

public class Employee {

	private String name;

	public String getName() {
		return name;
	}

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

Ciclo di vita del Bean di Spring – InitializingBean, DisposableBean

Creiamo una classe di servizio dove implementeremo entrambe le interfacce per i metodi di post-init e 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");
		}
	}
}

Ciclo di vita del Bean di Spring – post-init personalizzato, pre-destroy

Dato che non vogliamo che i nostri servizi dipendano direttamente dal framework Spring, creiamo un’altra forma di classe di servizio Employee dove avremo metodi di ciclo di vita Spring post-init e pre-destroy e li configureremo nel file di configurazione del 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");
	}

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

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

Guarderemo nel file di configurazione del bean Spring tra poco. Prima di questo creiamo un’altra classe di servizio che utilizzerà le annotazioni @PostConstruct e @PreDestroy.

Ciclo di vita del bean di primavera – @PostConstruct, @PreDestroy

Sotto è una classe semplice che verrà configurata come bean di primavera e per i metodi post-init e pre-destroy, stiamo utilizzando @PostConstruct e @PreDestroy annotazioni.

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

Ciclo di vita del bean di primavera – File di configurazione

Vediamo come configureremo i nostri bean nel file di contesto di primavera.

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

Nota che non sto inizializzando il nome dell’impiegato nella sua definizione di bean. Poiché EmployeeService sta utilizzando interfacce, non abbiamo bisogno di alcuna configurazione speciale qui. Per il bean MyEmployeeService, stiamo utilizzando gli attributi init-method e destroy-method per far sapere al framework di primavera i nostri metodi personalizzati da eseguire. La configurazione del bean MyService non ha nulla di speciale, ma come puoi vedere sto abilitando la configurazione basata su annotazioni per questo. La nostra applicazione è pronta, scriviamo un programma di test per vedere come vengono eseguiti i diversi metodi.

Ciclo di vita dei bean di Spring – Programma di 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");
	}

}

Quando eseguiamo il programma di test sopra, otteniamo l’output seguente.

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

Punti importanti del ciclo di vita dei bean di Spring:

  • Dall’output della console è chiaro che il contesto di Spring sta prima utilizzando il costruttore senza argomenti per inizializzare l’oggetto bean e poi chiamando il metodo post-init.
  • L’ordine di inizializzazione del bean è lo stesso in cui è definito nel file di configurazione del bean di Spring.
  • Il contesto viene restituito solo quando tutti i bean di Spring sono stati inizializzati correttamente con le esecuzioni dei metodi post-init.
  • Il nome dell’impiegato viene stampato come “Pankaj” perché è stato inizializzato nel metodo post-init.
  • Quando il contesto viene chiuso, i bean vengono distrutti nell’ordine inverso rispetto a quello in cui sono stati inizializzati, cioè in ordine LIFO (Last-In-First-Out).

Puoi decommentare il codice per ottenere il bean di tipo MyEmployeeService e confermare che l’output sarà simile e seguirà tutti i punti sopra menzionati.

Interfacce consapevoli di Spring

A volte abbiamo bisogno di oggetti del framework Spring nei nostri bean per eseguire alcune operazioni, ad esempio leggere i parametri ServletConfig e ServletContext o conoscere le definizioni dei bean caricate dall’ApplicationContext. Ecco perché il framework spring fornisce un insieme di interfacce *Aware che possiamo implementare nelle nostre classi bean. org.springframework.beans.factory.Aware è l’interfaccia marker radice per tutte queste interfacce *Aware. Tutte le interfacce *Aware sono sotto-interfacce di Aware e dichiarano un singolo metodo setter da implementare dal bean. Quindi il contesto spring utilizza l’iniezione di dipendenza basata su setter per iniettare gli oggetti corrispondenti nel bean e renderli disponibili per il nostro utilizzo. Le interfacce Spring Aware sono simili a ascoltatori servlet con metodi di richiamo e implementazione pattern design observer. Alcune delle interfacce *Aware importanti sono:

  • ApplicationContextAware – per iniettare l’oggetto ApplicationContext, ad esempio l’uso è per ottenere l’array dei nomi delle definizioni dei bean.
  • BeanFactoryAware – per iniettare l’oggetto BeanFactory, ad esempio l’uso è per controllare lo scope di un bean.
  • BeanNameAware – per conoscere il nome del bean definito nel file di configurazione.
  • ResourceLoaderAware – per iniettare l’oggetto ResourceLoader, l’esempio di utilizzo è ottenere lo stream di input per un file nel classpath.
  • ServletContextAware – per iniettare l’oggetto ServletContext nell’applicazione MVC, l’esempio di utilizzo è leggere i parametri e gli attributi del contesto.
  • ServletConfigAware – per iniettare l’oggetto ServletConfig nell’applicazione MVC, l’esempio di utilizzo è ottenere i parametri di configurazione del servlet.

Vediamo l’utilizzo di queste interfacce Aware in azione implementandone alcune in una classe che configureremo come bean di 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");
	}

}

File di configurazione di esempio di Spring *Aware

File di configurazione del bean di Spring molto semplice.

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

Programma di test di 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();
	}

}

Ora, quando eseguiamo la classe sopra, otteniamo l’output seguente.

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

L’output della console del programma di test è semplice da comprendere, non entrerò nei dettagli. Questo è tutto per i metodi del ciclo di vita dei bean di Spring e per l’iniezione di oggetti specifici del framework nei bean di Spring. Si prega di scaricare il progetto di esempio dal link sottostante e analizzarlo per saperne di più su di essi.

Scarica il progetto Ciclo di Vita di Spring Bean

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