Levenscyclus van Spring Bean

Vandaag zullen we kijken naar de levenscyclus van Spring Bean. Spring Beans zijn het belangrijkste onderdeel van elke Spring-toepassing. De Spring ApplicationContext is verantwoordelijk voor het initialiseren van de Spring Beans die zijn gedefinieerd in het configuratiebestand van de spring bean.

Spring Bean Life Cycle

De Spring Context is ook verantwoordelijk voor het injecteren van afhankelijkheden in de bean, hetzij via setter- of constructormethoden of door middel van spring-autobekabeling. Soms willen we resources initialiseren in de bean-klassen, zoals het maken van databaseverbindingen of het valideren van services van derden op het moment van initialisatie vóór een verzoek van een client. Het Spring-framework biedt verschillende manieren waarop we post-initialisatie- en pre-vernietigingsmethoden kunnen opgeven in de levenscyclus van een Spring-bean.

  1. Door InitializingBean en DisposableBean interfaces te implementeren – Beide deze interfaces verklaren een enkele methode waar we bronnen kunnen initialiseren/sluiten in de bean. Voor post-initialisatie kunnen we de InitializingBean interface implementeren en de afterPropertiesSet() methode implementeren. Voor pre-verwijdering kunnen we de DisposableBean interface implementeren en de destroy() methode implementeren. Deze methoden zijn de callback-methoden en vergelijkbaar met servlet-luisteraarimplementaties. Deze benadering is eenvoudig te gebruiken, maar het wordt niet aanbevolen omdat het een nauwe koppeling zal creëren met het Spring-framework in onze bean-implementaties.
  2. Het verstrekken van init-method en destroy-method attribuutwaarden voor de bean in het voorjaarsbean configuratiebestand. Dit is de aanbevolen aanpak vanwege geen directe afhankelijkheid van het Spring-framework en we kunnen onze eigen methoden maken.

Merk op dat zowel post-init als pre-destroy methoden geen argumenten moeten hebben, maar ze kunnen uitzonderingen gooien. We zouden ook de bean-instantie nodig hebben uit de Spring-toepassingscontext voor deze methoden oproep.

Spring Bean Levenscyclus – @PostConstruct, @PreDestroy Annotations

Spring framework ondersteunt ook de annotaties @PostConstruct en @PreDestroy voor het definiëren van post-initiële en pre-vernietigingsmethoden. Deze annotaties maken deel uit van het pakket javax.annotation. Om deze annotaties te laten werken, moeten we onze Spring-applicatie configureren om naar de annotaties te zoeken. Dit kunnen we doen door ofwel een bean van het type org.springframework.context.annotation.CommonAnnotationBeanPostProcessor te definiëren, of door het gebruik van het context:annotation-config-element in het configuratiebestand van de Spring-bean. Laten we een eenvoudige Spring-toepassing schrijven om het gebruik van bovenstaande configuraties voor het beheer van de levenscyclus van Spring-beans te demonstreren. Maak een Spring Maven-project in Spring Tool Suite; het uiteindelijke project zal eruitzien als de onderstaande afbeelding.

Spring Bean Life Cycle – Maven-afhankelijkheden

We hoeven geen extra afhankelijkheden toe te voegen voor het configureren van levenscyclusmethoden voor Spring-beans; ons pom.xml-bestand is vergelijkbaar met elk ander standaard Spring Maven-project.

<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 Life Cycle – Modelklasse

Laten we een eenvoudige Java Bean-klasse maken die zal worden gebruikt in serviceklassen.

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 Life Cycle – InitializingBean, DisposableBean

Laten we een serviceklasse maken waar we beide interfaces zullen implementeren voor post-init en pre-destroy-methoden.

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 Life Cycle – Custom post-init, pre-destroy

Aangezien we niet willen dat onze services rechtstreeks afhankelijk zijn van het Spring-framework, laten we een andere vorm van de Employee Service-klasse maken waar we post-init en pre-destroy Spring Life Cycle-methoden zullen hebben en we zullen ze configureren in het Spring bean-configuratiebestand.

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 methode
	public void destroy() throws Exception {
		System.out.println("MyEmployeeService Closing resources");
	}

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

We zullen zo meteen het Spring bean-configuratiebestand bekijken. Voordat we dat doen, laten we nog een andere serviceklasse maken die gebruik zal maken van @PostConstruct en @PreDestroy annotaties.

Spring Bean Life Cycle – @PostConstruct, @PreDestroy

Hieronder staat een eenvoudige klasse die geconfigureerd zal worden als een Spring bean en voor post-init en pre-destroy methoden, gebruiken we @PostConstruct en @PreDestroy annotaties.

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 Life Cycle – Configuratiebestand

Laten we eens kijken hoe we onze bonen zullen configureren in het Spring-contextbestand.

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

Let op dat ik de naam van de werknemer niet initialiseer in de definitie van de boon. Aangezien EmployeeService interfaces gebruikt, hebben we hier geen speciale configuratie nodig. Voor de MyEmployeeService-boon gebruiken we de init-method en destroy-method attributen om het Spring-framework te laten weten welke aangepaste methoden we moeten uitvoeren. De configuratie van de MyService-boon heeft niets bijzonders, maar zoals je kunt zien, activeer ik hier de op annotaties gebaseerde configuratie. Onze applicatie is klaar, laten we een testprogramma schrijven om te zien hoe verschillende methoden worden uitgevoerd.

Spring Bean Life Cycle – Test Program

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

}

Wanneer we het bovenstaande testprogramma uitvoeren, krijgen we de onderstaande uitvoer.

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

Belangrijke punten van de Spring Bean Life Cycle:

  • Uit de console-uitvoer blijkt dat de Spring Context eerst de no-args constructor gebruikt om het bean-object te initialiseren en vervolgens de post-init-methode aanroept.
  • De volgorde van bean-initialisatie is hetzelfde als die wordt gedefinieerd in het configuratiebestand van de spring bean.
  • De context wordt pas geretourneerd wanneer alle spring-beans correct zijn geïnitialiseerd met de uitvoering van de post-init-methode.
  • De naam van de werknemer wordt afgedrukt als “Pankaj” omdat deze is geïnitialiseerd in de post-init-methode.
  • Wanneer de context wordt gesloten, worden de bonen vernietigd in de omgekeerde volgorde waarin ze zijn geïnitialiseerd, dat wil zeggen in LIFO (Last-In-First-Out) volgorde.

U kunt de code uitcommentariëren om een bean van het type MyEmployeeService te krijgen en bevestigen dat de uitvoer vergelijkbaar zal zijn en alle bovengenoemde punten volgt.

Spring Aware Interfaces

Soms hebben we Spring Framework-objecten nodig in onze beans om bepaalde bewerkingen uit te voeren, bijvoorbeeld het lezen van ServletConfig- en ServletContext-parameters of om te weten welke beandefinities zijn geladen door de ApplicationContext. Daarom biedt het Spring-framework een heleboel *Aware-interfaces die we kunnen implementeren in onze bean-klassen. `org.springframework.beans.factory.Aware` is de hoofdmarkeringinterface voor al deze Aware-interfaces. Alle *Aware-interfaces zijn subinterfaces van Aware en verklaren een enkele setter-methode die door de bean moet worden geïmplementeerd. Vervolgens gebruikt de Spring-context setter-gebaseerde afhankelijkheidsinjectie om de overeenkomstige objecten in de bean te injecteren en deze beschikbaar te maken voor ons gebruik. Spring Aware-interfaces lijken op servlet listeners met callback-methoden en het implementeren van het observer-ontwerppatroon. Enkele van de belangrijke Aware-interfaces zijn:

  • ApplicationContextAware – om het ApplicationContext-object in te voegen, een voorbeeld van het gebruik is het verkrijgen van de array met bean-naamdefinities.
  • BeanFactoryAware – om het BeanFactory-object in te voegen, een voorbeeld van het gebruik is het controleren van de scope van een bean.
  • BeanNameAware – om de bean-naam te weten die is gedefinieerd in het configuratiebestand.
  • ResourceLoaderAware – om een ResourceLoader-object in te voegen, bijvoorbeeld om de inputstream voor een bestand in de classpath te verkrijgen.
  • ServletContextAware – om een ServletContext-object in te voegen in een MVC-toepassing, bijvoorbeeld om contextparameters en attributen te lezen.
  • ServletConfigAware – om een ServletConfig-object in te voegen in een MVC-toepassing, bijvoorbeeld om servletconfiguratieparameters te verkrijgen.

Latenn we deze Aware-interfaces in actie zien door er een paar van te implementeren in een klasse die we zullen configureren als 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 Voorbeeld Configuratiebestand

Heel eenvoudig voorbeeld van een spring bean-configuratiebestand.

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

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

}

Wanneer we nu de bovenstaande klasse uitvoeren, krijgen we de volgende uitvoer.

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

De console-uitvoer van het testprogramma is eenvoudig te begrijpen, ik zal daar niet te veel in detail op ingaan. Dat is alles voor de levenscyclusmethoden van de Spring Bean en het injecteren van frameworkspecifieke objecten in de Spring Beans. Download alsjeblieft het voorbeeldproject van de onderstaande link en analyseer het om meer over ze te leren.

Download Spring Bean Lifecycle Project

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