نطاقات Spring Bean تسمح لنا بالتحكم الأكثر تفصيلاً في إنشاء حالات الفاصلة للفول الصويا. في بعض الأحيان نريد إنشاء حالة الفول الصويا كفردي، ولكن في بعض الحالات الأخرى قد نرغب في إنشائها في كل طلب أو مرة واحدة في الجلسة.
نطاقات Spring Bean
هناك خمسة أنواع من نطاقات الفول الصويا في Spring:
- فردي – سيتم إنشاء مثيل واحد فقط من الفول الصويا في حاوية Spring. هذا هو النطاق الافتراضي للفول الصويا. عند استخدام هذا النطاق، تأكد من أن الفول ليس لديه متغيرات مشتركة أخرى وإلا قد يؤدي ذلك إلى مشاكل عدم اتساق البيانات.
- نموذج – سيتم إنشاء مثيل جديد في كل مرة يتم فيها طلب الفول من حاوية Spring.
- طلب – هذا هو نفس نطاق النموذج، ومع ذلك، يجب استخدامه لتطبيقات الويب. سيتم إنشاء مثيل جديد من الفول لكل طلب HTTP.
- جلسة – سيتم إنشاء فول جديد لكل جلسة HTTP من قبل الحاوية.
- جلسة عامة – يُستخدم هذا لإنشاء فول جلسة عامة لتطبيقات Portlet.
نطاقات Spring Bean Singleton و Prototype
يمكن استخدام نطاقات الـ Singleton و Prototype لـ Spring Bean في تطبيقات Spring المستقلة. دعنا نرى كيف يمكننا تكوين هذه النطاقات بسهولة باستخدام تعليق @Scope
. لنفترض أن لدينا فئة لفول الفاصوليا في جافا.
package com.journaldev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
لنقم بتعريف فئة تكوين Spring حيث سنقوم بتعريف الطريقة للحصول على مثيل MyBean من حاوية Spring.
package com.journaldev.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MyConfiguration {
@Bean
@Scope(value="singleton")
public MyBean myBean() {
return new MyBean();
}
}
لاحظ أن singleton
هو النطاق الافتراضي، لذلك يمكننا إزالة @Scope(value="singleton")
من تعريف الفاصوليا أعلاه. الآن دعونا نقوم بإنشاء طريقة رئيسية واختبار نطاق الـ singleton.
package com.journaldev.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MySpringApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(MyConfiguration.class);
ctx.refresh();
MyBean mb1 = ctx.getBean(MyBean.class);
System.out.println(mb1.hashCode());
MyBean mb2 = ctx.getBean(MyBean.class);
System.out.println(mb2.hashCode());
ctx.close();
}
}
عند تنفيذ البرنامج أعلاه، سنحصل على الناتج التالي.
MyBean instance created
867988177
867988177
لاحظ أن كلا مثيلات MyBean لهم نفس الكود المميز ويتم استدعاء البناء مرة واحدة، مما يعني أن حاوية Spring تقوم بإعادة نفس المثيل من MyBean دائمًا. الآن دعونا نغير النطاق إلى prototype
.
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
هذه المرة سنحصل على الناتج التالي عند تنفيذ الطريقة الرئيسية.
MyBean instance created
867988177
MyBean instance created
443934570
من الواضح أنه يتم إنشاء مثيل MyBean في كل مرة يُطلب فيها من حاوية Spring. الآن دعونا نغير النطاق إلى request
.
@Bean
@Scope(value="request")
public MyBean myBean() {
return new MyBean();
}
في هذه الحالة، سنحصل على استثناء التالي.
Exception in thread "main" java.lang.IllegalStateException: No Scope registered for scope name 'request'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:347)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1015)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
at com.journaldev.spring.MySpringApp.main(MySpringApp.java:12)
إنها بسبب أن مجالات الـ request
و session
و global-session
غير متوفرة لتطبيقات مستقلة.
نطاقات طلب Spring Bean والجلسة
لمثال نطاقات طلب وجلسة Spring Bean، سنقوم بإنشاء تطبيق ويب Spring Boot. أنشئ مشروع بداية Spring Boot واختر “ويب” حتى نتمكن من تشغيله كتطبيق ويب. سيبدو مشروعنا النهائي كالصورة أدناه.
ServletInitializer
و SpringBootMvcApplication
هما فئتان مُولدتان تلقائيًا من Spring Boot. لا داعي لإجراء أي تغييرات هناك. فيما يلي ملف pom.xml الخاص بي، انظر إلى التبعيات لتطبيقنا. قد يكون ملف pom.xml الخاص بك مختلفًا قليلاً استنادًا إلى إصدار Eclipse الذي تستخدمه.
<?xml version="1.0" encoding="UTF-8"?>
<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>com.journaldev.spring</groupId>
<artifactId>Spring-Boot-MVC</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Spring-Boot-MVC</name>
<description>Spring Beans Scope MVC</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>10</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
لنقم بإنشاء بعض المكونات الربيعية وتكوينها كفولحات ربيع في حاوية الربيع بنطاق request
و session
.
نطاق طلب Spring Bean
package com.journaldev.spring;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataRequestScope {
private String name = "Request Scope";
public DataRequestScope() {
System.out.println("DataRequestScope Constructor Called");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
نطاق جلسة Spring Bean
package com.journaldev.spring;
import org.springframework.context.annotation.Scope;
import java.time.LocalDateTime;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DataSessionScope {
private String name = "Session Scope";
public DataSessionScope() {
System.out.println("DataSessionScope Constructor Called at "+LocalDateTime.now());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Spring Component
الآن دعونا نقوم بإنشاء مكون Spring واستخدام Spring لتكوين الفولتات أعلاه تلقائيًا.
package com.journaldev.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Customer {
@Autowired
private DataRequestScope dataRequestScope;
@Autowired
private DataSessionScope dataSessionScope;
public DataRequestScope getDataRequestScope() {
return dataRequestScope;
}
public void setDataRequestScope(DataRequestScope dataRequestScope) {
this.dataRequestScope = dataRequestScope;
}
public DataSessionScope getDataSessionScope() {
return dataSessionScope;
}
public void setDataSessionScope(DataSessionScope dataSessionScope) {
this.dataSessionScope = dataSessionScope;
}
}
متحكم الراحة في Spring
وأخيرًا، دعونا نقوم بإنشاء فئة RestController وتكوين بعض نقاط نهاية واجهة برمجة التطبيق (API) لأغراض الاختبار.
package com.journaldev.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloData {
@Autowired
private Customer customer;
@RequestMapping("/nameRS")
public String helloRS() {
return customer.getDataRequestScope().getName();
}
@RequestMapping("/nameSSUpdated")
public String helloSSUpdated() {
customer.getDataSessionScope().setName("Session Scope Updated");
return customer.getDataSessionScope().getName();
}
@RequestMapping("/nameSS")
public String helloSS() {
return customer.getDataSessionScope().getName();
}
}
تكوين مهلة جلسة Spring Boot
أخيرًا، علينا تكوين متغيرات مهلة جلسة Spring Boot، أضف الخصائص التالية في src/main/resources/application.properties
.
server.session.cookie.max-age= 1
server.session.timeout= 1
الآن ستتم إبطال فولتات Spring الخاصة بنطاق الجلسة في دقيقة واحدة. قم بتشغيل فئة SpringBootMvcApplication
كتطبيق Spring Boot. يجب أن ترى الناتج أدناه لتكوين نقاط نهاية الخدمة الخاصة بنا.
2018-05-23 17:02:25.830 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameRS]}" onto public java.lang.String com.journaldev.spring.HelloData.helloRS()
2018-05-23 17:02:25.831 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSSUpdated]}" onto public java.lang.String com.journaldev.spring.HelloData.helloSSUpdated()
2018-05-23 17:02:25.832 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSS]}" onto public java.lang.String com.journaldev.spring.HelloData.helloSS()
اختبار نطاق طلب Spring Bean
افتح أي متصفح وانتقل إلى عنوان URL https://localhost:8080/nameRS
وتحقق من إخراج وحدة التحكم. يجب أن ترى DataRequestScope Constructor Called
يتم طباعته في كل طلب.
اختبار نطاق الجلسة Spring Bean
اذهب إلى https://localhost:8080/nameSS
وستحصل على النتائج التالية. الآن انتقل إلى
https://localhost:8080/nameSSUpdated
بحيث يتم تحديث قيمة الاسم DataSessionScope
إلى Session Scope Updated
. الآن انتقل مرة أخرى إلى
https://localhost:8080/nameSS
ويجب أن ترى القيمة المحدثة. بحلول هذا الوقت، يجب أن ترى
DataSessionScope Constructor Called at XXX
مرة واحدة فقط في مخرجات الوحدة. انتظر الآن لمدة دقيقة واحدة حتى يتم إبطال حاوية البيانات بالنطاق الزمني لجلستنا. ثم انتقل مرة أخرى إلى https://localhost:8080/nameSS
ويجب أن ترى النتائج الأصلية. كما يجب عليك التحقق من رسالة وحدة التحكم لإنشاء DataSessionScope مرة أخرى بواسطة الحاوية. هذا كل شيء بالنسبة للدرس عن نطاق الفاصوليا في الربيع.
يمكنك تنزيل مشروع Spring Beans Scope Spring Boot من مستودعنا على GitHub.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes