Автор оригинала: Loredana Crusoveanu.
1. Обзор
В этом кратком руководстве мы узнаем о различных типах областей бобов в Spring framework.
Область действия компонента определяет жизненный цикл и видимость этого компонента в контекстах, в которых мы его используем.
Последняя версия Spring framework определяет 6 типов областей:
- синглтон
- прототип
- запрос
- сессия
- приложение
- websocket
Последние четыре области, упомянутые выше, запрос, сеанс, приложение и websocket , доступны только в веб-приложении.
Дальнейшее чтение:
Что такое Весенняя фасоль?
Аннотации к весенним бобам
2. Одноэлементная область
Когда мы определяем боб с помощью области singleton , контейнер создает один экземпляр этого боба; все запросы на имя этого боба будут возвращать один и тот же объект, который кэшируется. Любые изменения объекта будут отражены во всех ссылках на компонент. Эта область является значением по умолчанию, если никакая другая область не указана.
Давайте создадим Лицо сущность, чтобы проиллюстрировать концепцию областей:
public class Person { private String name; // standard constructor, getters and setters }
После этого мы определяем боб с помощью singleton scope, используя аннотацию @Scope :
@Bean @Scope("singleton") public Person personSingleton() { return new Person(); }
Мы также можем использовать константу вместо значения String следующим образом:
@Scope(value = ConfigurableBeanFactory.SCOPE_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-определения используемых компонентов:
3. Область применения прототипа
Компонент с областью действия prototype будет возвращать другой экземпляр каждый раз, когда он запрашивается из контейнера. Он определяется путем установки значения prototype в аннотацию @Scope в определении компонента:
@Bean @Scope("prototype") public Person personPrototype() { return new Person(); }
Мы также можем использовать константу, как мы это делали для области singleton :
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Теперь мы напишем аналогичный тест, как и раньше, который показывает два объекта, запрашивающие одно и то же имя компонента с областью действия prototype . Они будут иметь разные состояния, поскольку они больше не ссылаются на один и тот же экземпляр компонента:
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 файл аналогичен файлу, представленному в предыдущем разделе, при добавлении определения xml для компонента с областью действия prototype :
4. Области с поддержкой Интернета
Как уже упоминалось ранее, существует четыре дополнительные области, доступные только в контексте веб-приложения. На практике мы используем их реже.
Область request создает экземпляр компонента для одного HTTP-запроса, в то время как область s ession создает экземпляр компонента для сеанса HTTP.
Область application создает экземпляр компонента для жизненного цикла ServletContext , а область websocket создает его для конкретного сеанса WebSocket .
Давайте создадим класс, который будет использоваться для создания экземпляров компонентов:
public class HelloMessageGenerator { private String message; // standard getter and setter }
4.1. Объем запроса
Мы можем определить компонент с помощью request scope, используя аннотацию @Scope :
@Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }
Атрибут proxy Mode необходим, поскольку в момент создания экземпляра контекста веб-приложения активный запрос отсутствует. Spring создает прокси-сервер, который будет введен в качестве зависимости, и создает экземпляр целевого компонента, когда он необходим в запросе.
Мы также можем использовать @RequestScope составленную аннотацию, которая действует как ярлык для приведенного выше определения:
@Bean @RequestScope public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }
Затем мы можем определить контроллер, который имеет введенную ссылку на компонент с областью действия запроса . Нам нужно дважды получить доступ к одному и тому же запросу, чтобы протестировать конкретные области веб-приложений.
Если мы будем отображать сообщение каждый раз, когда выполняется запрос , мы увидим, что значение сбрасывается на null , даже если позже оно будет изменено в методе. Это происходит из-за того, что для каждого запроса возвращается другой экземпляр компонента.
@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"; } }
4.2. Объем сеанса
Мы можем определить компонент с помощью session scope аналогичным образом:
@Bean @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }
Существует также специальная составная аннотация, которую мы можем использовать для упрощения определения компонента:
@Bean @SessionScope public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }
Далее мы определяем контроллер со ссылкой на компонент с областью действия сеанса . Опять же, нам нужно выполнить два запроса, чтобы показать, что значение поля message одинаково для сеанса.
В этом случае, когда запрос выполняется в первый раз, значение message равно null. Однако после изменения это значение сохраняется для последующих запросов, поскольку один и тот же экземпляр компонента возвращается для всего сеанса.
@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"; } }
4.3. Область применения
Область application создает экземпляр компонента для жизненного цикла ServletContext.
Это похоже на область действия singleton , но есть очень важное различие в отношении области действия компонента.
Когда бобы имеют область действия application , один и тот же экземпляр боба совместно используется несколькими приложениями на основе сервлетов , работающими в одном и том же ServletContext , в то время как бобы с областью действия singleton ограничены только одним контекстом приложения.
Давайте создадим боб с помощью application scope:
@Bean @Scope( value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator(); }
Аналогично областям request и session , мы можем использовать более короткую версию:
@Bean @ApplicationScope public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator(); }
Теперь давайте создадим контроллер, который ссылается на этот компонент:
@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"; } }
В этом случае после установки в applicationScopedBean значение message будет сохранено для всех последующих запросов, сеансов и даже для различных приложений сервлетов , которые будут обращаться к этому компоненту, при условии, что он работает в одном и том же ServletContext.
4.4. Область действия WebSocket
Наконец, давайте создадим боб с областью websocket :
@Bean @Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator websocketScopedBean() { return new HelloMessageGenerator(); }
При первом доступе компоненты WebSocket с областью действия хранятся в атрибутах сеанса WebSocket . Один и тот же экземпляр компонента затем возвращается всякий раз, когда к этому компоненту обращаются в течение всего сеанса WebSocket .
Мы также можем сказать, что он демонстрирует одноэлементное поведение, но ограничивается только сеансом W websocket .
5. Заключение
В этой статье мы обсудили различные области применения компонентов, предоставляемые Spring, и их предполагаемое использование.
С реализацией этой статьи можно ознакомиться в проекте GitHub .