מחזור חיי החרוז של Spring

היום נבחן את מחזור החיים של Spring Bean. Spring Beans הם החלק החשוב ביותר של כל יישום Spring. Spring ApplicationContext אחראית לאתחול Spring Beans המוגדרים בקובץ התצורה של הפול Bean של Spring.

מחזור חיי Spring Bean

ההקשר של Spring אחראי גם על תלות הזרזות בפול Bean, באמצעות שיטות setter או בנאי או על ידי autowiring של Spring. לפעמים אנו רוצים לאתחל משאבים במחלקות הפול Bean, לדוגמה ליצור חיבורי מסד נתונים או לאמת שירותי צד שלישי בזמן האתחול לפני בקשת לקוח כלשהי. הגישה של Spring מספקת דרכים שונות דרךן ניתן לספק שיטות אחרי-אתחול ופרה-הרס במחזור חיי Spring Bean.

  1. על ידי הטמעת ממשקי InitializingBean ו־DisposableBean – שני ממשקים אלו מצהירים על שיטה יחידה שבאמצעותה נוכל לאתחל / לסגור משאבים בחומרה. לאחר אתחול, אנו יכולים לממש את ממשק InitializingBean ולספק יישום של שיטת afterPropertiesSet(). לצורך השמדה מוקדמת, אנו יכולים לממש את ממשק DisposableBean ולספק יישום של שיטת destroy(). שתי אלו הן שיטות קריאת חזרה ודומות ליישומים של מאזני סף של סרבלים. הגישה הזו פשוטה לשימוש, אך אין להמליץ עליה מכיוון שהיא יוצרת קשר צמוד עם מסגרת ה־Spring ביישומי החומרה שלנו.
  2. ספק init-method ו־destroy-method ערכי מאפייני הפעולה למערכת החומרה בקובץ תצורת החומרה של Spring. זו הגישה המומלצת בשל חוסר התלות הישירה במסגרת ה־Spring והאפשרות ליצירת שיטות משלנו.

שים לב שכמו כן post-init ו־pre-destroy יש להם לא לקבל פרמטרים אך יכולים לזרוק יוצא דופן. יש גם צורך לקבל את המופע של החומרה מההקשר של אפליקציית ה־Spring לקריאת שתי השיטות אלו.

מחזור חיי חומרה ב-Spring – @PostConstruct, @PreDestroy הערות

הגדרת Spring framework תומכת גם באנוטציות @PostConstruct ו- @PreDestroy להגדרת שיטות לאחר-איתחול ולפני-הרס. אלו אנוטציות שנמצאות בחבילת javax.annotation. עם זאת, על מנת שהאנוטציות הללו יעבדו, עלינו להגדיר את אפליקציית Spring שלנו כך שתחפש אנוטציות. אנו יכולים לעשות זאת או על ידי הגדרת פול סוג org.springframework.context.annotation.CommonAnnotationBeanPostProcessor או על ידי הרכבת אלמנט context:annotation-config בקובץ התצורה של הפסקת הבונים של Spring. בואו נכתוב אפליקציית Spring פשוטה כדי להדגיש את השימוש בהגדרות הנ"ל לניהול מחזור חיי הפסקות הבונים של Spring. ניצור פרויקט Maven של Spring ב- Spring Tool Suite, והפרויקט הסופי יראה כמו בתמונה למטה.

מחזור חיי הפסקות ב-Spring – תלותי Maven

אנו לא צריכים לכלול תלות נוספות להגדרת שיטות מחזור חיי הפסקות ב-Spring, קובץ ה-pom.xml שלנו דומה לכל פרויקט 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 – מחלקת מודל

בואו ניצור קלאס 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 – 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 – Custom post-init, pre-destroy

מאחר ואנו לא רוצים שיש לשירותים שלנו תלות ישירה בספריית הקיץ של Spring, בואו ניצור צורה נוספת של קלאס שירות של עובדים שבו נהיה לנו שיטות מעגל החיים של Spring לאחר-איתחול ולפני-השמדה, ונקבע אותן בקובץ התצורה של הבונים של 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");
	}

	// שיטת לפני השמדה
	public void destroy() throws Exception {
		System.out.println("MyEmployeeService Closing resources");
	}

	// שיטת אחרי איתחול
	public void init() throws Exception {
		System.out.println("MyEmployeeService initializing to dummy value");
		if(employee.getName() == null){
			employee.setName("Pankaj");
		}
	}
}

נסתכל על קובץ התצורה של הבונים של Spring ברגע. לפני זאת בואו ניצור קלאס שירות נוסף שישתמש בהערות @PostConstruct ו־@PreDestroy.

מחזור חיי החפץ בספרינג – @PostConstruct, @PreDestroy

למטה יש כיתה פשוטה שתוגדר כחפץ בספרינג ועבור שיטות לאחר האתחול ולפני ההרס, אנו משתמשים ב- @PostConstruct וב- @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");
	}
}

מחזור חיי החפץ בספרינג – קובץ הגדרת התצורה

בואו נראה איך נגדיר את החפצים שלנו בקובץ ההקשר של ספרינג.

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

שימו לב שאני לא מאתחל את שם העובד בהגדרת החפץ שלו. מאחר ש- EmployeeService משתמש בממשקים, אין צורך בהגדרה מיוחדת כאן. עבור החפץ MyEmployeeService, אנו משתמשים במאפייני init-method ו- destroy-method כדי להודיע למערכת הספרינג על שיטות המותאמות שלנו לביצוע. הגדרת החפץ של MyService אין לה שום דבר מיוחד, אך כפי שניתן לראות שאני מפעיל את ההגדרה המבוססת על הערות עבור זה. היישום שלנו מוכן, בואו נכתוב תוכנית בדיקה כדי לראות איך נבצע שונות בין השיטות השונות.

מחזור חיי פס האביב – תוכנית בדיקה

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

נקודות חשובות במחזור חיי פס האביב:

  • מהפלט של המקלט ברור כי הקשר של פס האביב משתמש תחילה בבנאי ללא פרמטרים כדי לאתחל את אובייקט הפס ואז מתקשר לשיטת post-init.
  • סדר אתחול הפס זהה לכפי שהוא מוגדר בקובץ התצורה של פס האביב.
  • הקשר מוחזר רק כאשר כל פסי האביב מאותחלים בהתאם עם ביצועי שיטת post-init.
  • שם העובד מודפס כ "פנקג 'כי הוא אותחל בשיטת post-init.
  • כאשר ההקשר נסגר, הפסים נהרסים בסדר ההפוך לזה שבו הם אותחלו, כלומר בסדר LIFO (הכניסה האחרונה יצאת ראשונה).

ניתן להסיר את ההערה מהקוד כדי לקבל פס מסוג MyEmployeeService ולאשר שהפלט יהיה דומה ויעקוב אחר כל הנקודות שהוזכרו למעלה.

ממשקי מודע פס האביב

לפעמים אנו זקוקים לאובייקטים של מסגרת Spring בתוך ה-Beans שלנו כדי לבצע פעולות מסוימות, לדוג, קריאה לפרמטרים של ServletConfig ו-ServletContext או לידע על דפוסי ה-Bean שנטענו על ידי ה-ApplicationContext. לכן, מסגרת Spring מספקת קבוצה של ממשקים *Aware שאנו יכולים ליישם במחלקות ה-Bean שלנו. org.springframework.beans.factory.Aware הוא ממשק הסימון הבסיסי לכל אלו. כל ממשקי ה-*Aware הם ממשקי משנה של Aware ומציינים שיטת setter יחידה שיש ליישם על ידי ה-Bean. לאחר מכן, ההקשר של Spring משתמש בהזנת תלות באמצעות setter כדי להכניס את האובייקטים המתאימים ל-Bean ולהפוך אותם לזמינים לשימוש שלנו. ממשקי Aware של Spring דומים ל־אזניות Servlet עם שיטות הזרקת קרוב ומיישמים את דפוס עיצוב התצפית. חלק מהממשקים החשובים של Aware הם:

  • ApplicationContextAware – להזין אובייקט ApplicationContext, שימוש דוגמא הוא לקבל מערך שמות הגדרות ה-Bean.
  • BeanFactoryAware – להזין אובייקט BeanFactory, שימוש דוגמא הוא לבדוק טווח של Bean.
  • BeanNameAware – לדעת את שם ה-Bean המוגדר בקובץ התצורה.
  • ResourceLoaderAware – להכניס אובייקט ResourceLoader, שימוש דוגמה הוא לקבלת נתוני הקלט עבור קובץ במסלול הכיתות.
  • ServletContextAware – להכניס אובייקט ServletContext ביישום MVC, שימוש דוגמה הוא לקרוא פרמטרי הקשר ותכונות.
  • ServletConfigAware – להכניס אובייקט ServletConfig ביישום MVC, שימוש דוגמה הוא לקבל פרמטרים של תצורת סרוולט.

בואו נראה איך משתמשים בממשקי Aware בפעולה על ידי מימוש כמה מהם במחלקה שנקבע כערך בפלטפורמת 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");
	}

}

דוגמת קובץ הגדרת Spring *Aware

קובץ הגדרת פלטפורמת 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

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 והכנסת עצמים מסגרתיים מסוימים אל תוך הפקודות של Spring. אנא הורידו את הפרויקט לדוגמה מהקישור למטה ונתחיל בניתוח כדי ללמוד עוד על זה.

הורד את פרויקט מעגל חיי פולי Spring

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