اليوم سنتطرق إلى دورة حياة Spring Bean. Spring Beans هي الجزء الأكثر أهمية في أي تطبيق Spring. Spring ApplicationContext مسؤول عن تهيئة Spring Beans المعرفة في ملف تكوين الفاصلة الربيعية.
Spring Bean Life Cycle
Spring Context مسؤول أيضًا عن حقن التبعيات في الفاصلة ، إما من خلال وسائل setter أو constructor أو عن طريق التشغيل التلقائي للربيع. في بعض الأحيان نريد تهيئة الموارد في فئات الفاصلة ، على سبيل المثال إنشاء اتصالات قاعدة البيانات أو التحقق من خدمات الطرف الثالث في وقت التهيئة قبل أي طلب من العميل. تقدم إطار الربيع طرقًا مختلفة من خلالها يمكننا توفير الطرق ما بعد التهيئة وطرق التدمير المسبقة في دورة حياة الفاصلة الربيعية.
- بتنفيذ الواجهتين InitializingBean و DisposableBean – كلتا هاتين الواجهتين تعلنان عن طريقة واحدة حيث يمكننا تهيئة/إغلاق الموارد في الكائن. للتهيئة بعد التهيئة، يمكننا تنفيذ واجهة
InitializingBean
وتوفير تنفيذ لطريقةafterPropertiesSet()
. بالنسبة للتدمير السابق للكائن، يمكننا تنفيذ واجهةDisposableBean
وتوفير تنفيذ لطريقةdestroy()
. هذه الطرق هي طرق الاتصال ومشابهة لتنفيذات مستمعات السيرفليت. هذا النهج سهل الاستخدام ولكن لا يُوصى به لأنه سيخلق ارتباطًا وثيقًا مع إطار العمل Spring في تنفيذات الكائنات الخاصة بنا. - من خلال تقديم قيم السمات init-method و destroy-method للكائن في ملف تكوين الحبة الربيعية. هذا هو النهج الموصى به بسبب عدم وجود تبعية مباشرة إلى إطار Spring ويمكننا إنشاء طرقنا الخاصة.
يرجى ملاحظة أن كل من الطرق post-init و pre-destroy يجب أن تكون بدون معاملات ولكن يمكنها رمي استثناءات. سنحتاج أيضًا إلى الحصول على مثيل الحبة من سياق التطبيق الربيعي لاستدعاء هذه الطرق.
دورة حياة الحبة الربيعية – @PostConstruct، @PreDestroy الشروح
الإطار الربيعي يدعم أيضًا التعليمات البرمجية @PostConstruct
و @PreDestroy
لتحديد الطرق بعد التهيئة وقبل الإتلاف. هذه التعليمات البرمجية جزء من حزمة javax.annotation
. ومع ذلك، لتعمل هذه التعليمات البرمجية، نحتاج إلى تكوين تطبيق الربيع الخاص بنا للبحث عن التعليمات البرمجية. يمكننا القيام بذلك إما عن طريق تحديد حبوب من نوع org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
أو عن طريق عنصر context:annotation-config
في ملف تكوين الفاصلة الربيعي. دعونا نكتب تطبيقًا بسيطًا للربيع لعرض استخدام التكوينات أعلاه لإدارة دورة حياة الفاصلة الربيعية. قم بإنشاء مشروع ربيع Maven في Spring Tool Suite، وسيكون المشروع النهائي مشابهًا للصورة أدناه.
دورة حياة الفاصلة الربيعية – تبعيات مافن
لسنا بحاجة إلى تضمين أي تبعيات إضافية لتكوين طرق دورة حياة الفاصلة الربيعية، ملف 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>
دورة حياة الفاصلة الربيعية – الفئة النموذجية
لنقم بإنشاء فئة 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
لنقم بإنشاء فئة خدمة حيث سنقوم بتنفيذ كلا الواجهتين لأساليب ما بعد التهيئة وقبل الإتلاف.
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 ، فلنقم بإنشاء نموذج آخر لفئة خدمة الموظف حيث سيكون لدينا أساليب دورة حياة 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");
}
}
}
سنلقي نظرة على ملف تكوين حبة الربيع في لحظة. قبل ذلك، دعنا نقوم بإنشاء فئة خدمة أخرى ستستخدم التعليمات البرمجية @PostConstruct و @PreDestroy.
دورة حياة Spring Bean – @PostConstruct, @PreDestroy
أدناه هو فئة بسيطة ستتم تكوينها كـ حبة Spring ولأساليب ما بعد التهيئة وقبل تدميرها، نستخدم @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");
}
}
دورة حياة Spring Bean – ملف التكوين
دعنا نرى كيف سنقوم بتكوين حباتنا في ملف السياق الربيعي.
<?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 = 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 Aware
أحيانًا نحتاج إلى كائنات Spring Framework في حاويات الفول لأداء بعض العمليات، على سبيل المثال قراءة معلمات ServletConfig وServletContext أو معرفة تعريفات الفول التي تم تحميلها بواسطة ApplicationContext. لهذا السبب، يوفر إطار العمل Spring مجموعة من واجهات *Aware التي يمكننا تنفيذها في فئات الفول الخاصة بنا. org.springframework.beans.factory.Aware
هي واجهة العلامة الجذرية لجميع هذه الواجهات *Aware. جميع واجهات *Aware هي واجهات فرعية لـ Aware وتعلن عن أسلوب setter واحد ليتم تنفيذه بواسطة الفول. بعد ذلك، يستخدم سياق Spring حقن الاعتماد القائم على الsetter لحقن الكائنات المقابلة في الفول وجعلها متاحة للاستخدام. واجهات Spring Aware مشابهة لـ مستمعي servlet مع أساليب الرد الفعل وتنفيذ نمط التصميم المراقب. بعض واجهات Aware المهمة هي:
- ApplicationContextAware – لحقن كائن ApplicationContext، مثال على الاستخدام هو الحصول على مصفوفة من أسماء تعريف الفول.
- BeanFactoryAware – لحقن كائن BeanFactory، مثال على الاستخدام هو التحقق من نطاق الفول.
- BeanNameAware – لمعرفة اسم الفول المعرف في ملف التكوين.
- ResourceLoaderAware – لحقن كائن ResourceLoader، مثال على الاستخدام هو الحصول على تدفق الإدخال لملف في classpath.
- ServletContextAware – لحقن كائن ServletContext في تطبيق MVC، مثال على الاستخدام هو قراءة معلمات وسمات السياق.
- ServletConfigAware – لحقن كائن ServletConfig في تطبيق MVC، مثال على الاستخدام هو الحصول على معلمات تكوين السيرفلت.
لنرى استخدامات هذه الواجهات Aware في العمل من خلال تنفيذ بعضها في صف نقوم بتكوينه كفاصلة ربيعية.
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 مثال ملف التكوين
ملف تكوين بسيط جدا لفاصلة ربيعية.
<?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
إخراج وحدة التحكم في الإخراج البرنامج الاختباري بسيط للفهم، لن أدخل في تفاصيل كثيرة حول ذلك. هذا كل شيء بالنسبة لطرق دورة حياة فاصلة ربيعية وحقن كائنات إطار العمل الخاصة في فاصلات ربيعية. يرجى تنزيل المشروع التوضيحي من الرابط أدناه وتحليله لمعرفة المزيد حولها.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-life-cycle