Scope چیست ؟ در Spring هر bean میتواند چرخه عمر و میدان دید متفاوتی را داشته باشد که بسته به نیازمان میتوانیم از آنها استفاده کنیم تا نسخه کنونی Spring (نسخه 5) 6 نوع scope در Spring تعریف شده است :
- Singleton
- Prototype
- Request
- Session
- Application
- Websocket
Singleton Scope : کلیه bean های singleton در Spring Container بصورت singleton تنها یک نمونه از آن bean موجود است و هر باری که آن bean استفاده میشود بعد از استفاده از بین نمیرود (تا زمانی که Spring Container بالا باشد) و بصورت cache نمونه آن نگه داری شده است. پس هر تغییری در singleton bean باعث میشود روی بقیه reference ها هم تاثیر داشته باشد. در Spring اگر نوع Scope را مشخص نکرده باشیم بصورت پیش فرض singleton در نظر گرفته میشود
//simple entity class public class Person { private String name; // standard constructor, getters and setters }
@Bean @Scope("singleton") public Person personSingleton() { return new Person(); }
همچنین میتوان بجای نوشتن عبارت "singleton" از ثابت آن هم استفاده کرد
@Bean @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) public Person personSingleton() { return new Person(); }
حالا میتوانیم Singleton بودن آنرا چک کنیم حتی وقتی که وضعیت درونی یکی از نمونه هایش را تغییر دهیم :
private static final String NAME = "John Smith"; @Test public void givenSingletonScope_whenSetName_thenEqualNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personSingletonA = (Person) applicationContext.getBean("personSingleton"); Person personSingletonB = (Person) applicationContext.getBean("personSingleton"); personSingletonA.setName(NAME); Assert.assertEquals(NAME, personSingletonB.getName()); ((AbstractApplicationContext) applicationContext).close(); }
فایل scopes.xml باید شامل تنظیمات زیر باشد :
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="personSingleton" class="org.baeldung.scopes.Person" scope="singleton"/> </beans>
Property Scope : هر باری که یک نمونه از این bean ساخته شود یک Object جدید است و state آن به بقیه نمونه های ساخته شده ارتباطی نخواهد داشت
@Bean @Scope("prototype") public Person personPrototype() { return new Person(); }
@Bean @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Person personPrototype() { return new Person(); }
تست کردن نمونه های شاخته شده که reference جداگانه ای دارند :
private static final String NAME = "John Smith"; private static final String NAME_OTHER = "Anna Jones"; @Test public void givenPrototypeScope_whenSetNames_thenDifferentNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personPrototypeA = (Person) applicationContext.getBean("personPrototype"); Person personPrototypeB = (Person) applicationContext.getBean("personPrototype"); personPrototypeA.setName(NAME); personPrototypeB.setName(NAME_OTHER); Assert.assertEquals(NAME, personPrototypeA.getName()); Assert.assertEquals(NAME_OTHER, personPrototypeB.getName()); ((AbstractApplicationContext) applicationContext).close(); }
تنظیمات مربوط به فایل scopes.xml :
<bean id="personPrototype" class="org.baeldung.scopes.Person" scope="prototype"/>
Scope های مربوط به WEB :
4 scope بعدی مربوط به web میباشند که در ادامه به آنها میپردازیم ابتدا کلاس ساده زیر را در نظر بگیرید :
public class HelloMessageGenerator { private String message; // standard getter and setter }
Request Scope :
@Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }
* در مورد کد بالا اول این توضیح را باید داد که متغییر proxyMode لازم است چرا که در لحظه ایجاد web application context هیچ request فعالی وجود ندارد برای همین Spring با استفاده از یک proxy آنرا به عنوان یک وابستگی تزریق خواهد کرد و موقعی که در آینده درخواستی به این bean رسید آن bean را ایجاد میکند
بعد از تعریف bean بالا یک controller ایجاد میکنیم که یک رفرنس از bean بالا را در خودش دارد. هر بار که Message جدیدی ارسال شود میتوانیم مشاهده کنیم که مقدارش به صورت null خواهد شد پس میتوانیم نتیجه بگیریم که bean های Request Scope به ازای هر بار درخواست ایجاد و بعد از بین می روند چیزی مشابه property scope که قبلا به آن پرداختیم
@Controller public class ScopesController { @Resource(name = "requestScopedBean") HelloMessageGenerator requestScopedBean; @RequestMapping("/scopes/request") public String getRequestScopeMessage(final Model model) { model.addAttribute("previousMessage", requestScopedBean.getMessage()); requestScopedBean.setMessage("Good morning!"); model.addAttribute("currentMessage", requestScopedBean.getMessage()); return "scopesExample"; } }
Session Scope :
نحوه ایجاد :
@Bean @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }
در این scope هنگامی که request ای برای اولین بار دریافت میشود این bean یک نمونه ازش ساخته میشود و در subrequest های بعدی از همان Client , نمونه قابل دستیابی است از این scope میتوان زمانی که میخواهیم state درخواست های یک کلاینت را نگه داریم استفاده میکنیم
@Controller public class ScopesController { @Resource(name = "sessionScopedBean") HelloMessageGenerator sessionScopedBean; @RequestMapping("/scopes/session") public String getSessionScopeMessage(final Model model) { model.addAttribute("previousMessage", sessionScopedBean.getMessage()); sessionScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", sessionScopedBean.getMessage()); return "scopesExample"; } }
Application Scope : در این حالت bean ها یکبار ابتدای شروع چرخه عمر Servlet Context ساخته میشوند و تا انتهای چرخه عمر نگه داری میشوند و همانند singleton تنها یک نمونه از bean وجود خواهد داشت
@Bean @Scope( value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator(); }
ایجاد Controller :
@Controller public class ScopesController { @Resource(name = "applicationScopedBean") HelloMessageGenerator applicationScopedBean; @RequestMapping("/scopes/application") public String getApplicationScopeMessage(final Model model) { model.addAttribute("previousMessage", applicationScopedBean.getMessage()); applicationScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", applicationScopedBean.getMessage()); return "scopesExample"; } }
تا زمانی که درخواست ها در یک Servlet Context باشند همگی به یک نمونه از bean دسترسی خواهند داشت و بین همه درخواست ها مشترک خواهد بود
WebSocket Scope :
@Bean @Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator websocketScopedBean() { return new HelloMessageGenerator(); }
با اولین درخواست یک نمونه از ان در WebSocket Session Attributes ایجاد و نگه داری میشود و در طول WebSocket Session آن bean قابل دسترس است. شاید بتوان با Singleton مقایسه کرد ولی این تفاوت را دارد که چرخه عمر bean ایجاد شده تنها در یک WebSocket Session وجود خواهد داشت