Статьи

Учебное пособие по JSF — Сеанс JSF / CDI

Область действия сеанса существует в нескольких циклах запросов-ответов HTTP (теоретически неограниченно).

jsf cdi omnifaces и deltaspike области действия - область действия сеанса

Область запросов очень полезна в любом веб-приложении, когда требуется одно взаимодействие на цикл HTTP-запрос-ответ. Однако, когда вам нужны объекты, видимые для любого цикла HTTP-запроса-ответа, принадлежащего сеансу пользователя, тогда вам нужна область сеанса ; в этом случае bean-компонент живет столько же, сколько и HTTP-сеанс. Область действия сеанса позволяет создавать и привязывать объекты к сеансу. Он создается при первом HTTP-запросе с участием этого компонента в сеансе и уничтожается при аннулировании сеанса HTTP. Область запроса присутствует в JSF и CDI и функционирует аналогичным образом. Он может быть использован для не насыщенных запросов AJAX и не AJAX.

Аннотации для сессий

JSF : аннотацией области действия запроса JSF является @SessionScoped ( javax.faces.bean.SessionScoped ). @ManagedBean с этой областью действия должен быть аннотирован @ManagedBean ( javax.faces.bean.ManagedBean ). Область по умолчанию — @RequestScope .

CDI : аннотацией области действия запроса CDI является @SessionScoped ( javax.enterprise.context.SessionScoped ). @Named с такой областью действия должен быть аннотирован @Named ( javax.inject.Named ). Для управляемых компонентов CDI ( @Named ) @Named по умолчанию является псевдо-область @Dependent .

Простой пример

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// index.xhtml
<h:body> 
 <h4>Same view after submit (index.xhtml):</h4>
 <h:form>
  <h:commandButton value="Count" action="#{countBean.countActionVoid()}"/>
 </h:form>
 Current value: #{countBean.count}
 
 <h4>Forward to another view after submit (count.xhtml):</h4>
 <h:form>
  <h:commandButton value="Count" action="#{countBean.countActionAndForward()}"/>
 </h:form>
 Current value: #{countBean.count}
 
 <h4>Redirect to another view after submit (count.xhtml):</h4>
 <h:form>
  <h:commandButton value="Count" action="#{countBean.countActionAndRedirect()}"/>
 </h:form>
 Current value: #{countBean.count}
 
 <h4>AJAX :</h4>
 <h:form>
  <h:commandButton value="Count" action="#{countBean.countActionVoid()}">
   <f:ajax render="currentValueId"/>
  </h:commandButton>
 </h:form>
 <h:outputText id="currentValueId" value="Current value: #{countBean.count}"/>
</h:body>
 
// count.xhtml
<h:body>          
 Current value: #{countBean.count}       
</h:body>
 
// CountBean.java
import java.util.logging.Logger;
import java.io.Serializable;
// for JSF
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
// for CDI
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
 
// JSF            vs            CDI
@ManagedBean                    @Named
@SessionScoped                  @SessionScoped
public class CountBean implements Serializable {
 
 private static final Logger LOG = Logger.getLogger(CountBean.class.getName());
 
 private int count;
 
 public CountBean() {
  LOG.info("CountBean#Initializing counter ...");
  count = 0;
 }
 
 public void countActionVoid() {
  LOG.info("CountBean#countActionVoid() - Increasing counter ...");
  count++;
 }
    
 public String countActionAndForward() {
  LOG.info("CountBean#countActionAndForward() - Increasing counter ...");
  count++;
  return "count";
 }
    
 public String countActionAndRedirect() {
  LOG.info("CountBean#countActionAndRedirect() - Increasing counter ...");
  count++;
  return "count?faces-redirect=true;";
 }
 
 public int getCount() {
  return count;
 }
 
 public void setCount(int count) {
  this.count = count;
 }
}

Полное приложение доступно здесь .

Таким образом, при навигации через AJAX, через механизм пересылки назад в том же виде (или другом виде) или механизме перенаправления значение count будет увеличено на 1 . Это раскрывает два аспекта:

  • Конструктор CountBean вызывается для создания нового экземпляра один раз за сеанс пользователя. Это означает, что count инициализируется с 0 только один раз. Дальнейшие запросы, запущенные в текущем сеансе пользователя, будут использовать этот экземпляр CountBean . Мы говорим, что для каждого пользователя CountBean экземпляр CountBean .
  • Область сеанса не теряет состояние объекта при пересылке или перенаправлении. Состояние объекта доступно до тех пор, пока session будет уничтожен (например, время ожидания сеанса, недействительность и т. Д.).

В основном вы должны обращать внимание, когда вы отправляете данные в сессионный компонент. Представленные данные будут «жить» столько, сколько текущий пользовательский сеанс. Итак, хорошая практика скажет вам не хранить в сеансе большое количество данных, особенно если память является критическим ресурсом.

Реализует Сериализуемый

Управляемые компоненты JSF и CDI должны быть объявлены как Serializable ( implements Serializable ). Это необходимо, поскольку контейнер может сохранить (сериализовать) данные сеанса на жесткий диск. Это позволяет контейнеру управлять критическими ситуациями, такими как тяжелая загрузка, или просто обмениваться данными с другими серверами в кластере или восстанавливать сеансы во время перезапуска сервера.

Session Scope Программный доступ

Программно вы можете взаимодействовать с областью сеанса следующим образом:

  • получить доступ к карте области сеанса
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    // JSF 2.0-2.2
    FacesContext context = FacesContext.getCurrentInstance();
    Map<String, Object> requestMap = context.getExternalContext().getSessionMap();
     
    // JSF 2.3
    @Inject
    @SessionMap
    private Map<String, Object> sessionMap;
     
    // OmniFaces
    Map<String, Object> requestmap = Faces.getSessionMap();
  • получить атрибут области сеанса
    1
    2
    3
    4
    5
    // JSF 2.0 - 2.3
    sessionMap.put(name, value);
     
    // OmniFaces
    Faces.setSessionAttribute(name, value);
  • удалить атрибут области сеанса
    1
    2
    3
    4
    5
    // JSF 2.0-2.3
    Object value = sessionMap.remove(name);
     
    // OmniFaces
    <T> value = Faces.removeSessionAttribute(name);

! На страницах JSF вы можете использовать неявный объект #{sessionScope} (например, получить экземпляр CountBean : #{sessionScope.countBean} ).

Среди прочего, карта сеанса будет содержать экземпляры управляемых bean-компонентов, объявленных в области сеанса ( @SessionScoped (JSF/CDI )).

В случае JSF-управляемых bean-компонентов (не CDI-управляемых bean-компонентов — в этом случае ключи довольно сложны), вы можете легко идентифицировать такие bean-компоненты по их именам, которые становятся ключами в карте сеанса. Поэтому вы сможете найти экземпляр этого управляемого JSF-компонента в карте сеанса под ключом countBean . Если вы указываете имя компонента через @ManagedBean (name = » some_name «), то some_name будет ключом в карте сеанса. Таким образом, с помощью карты сеансов вы можете получить доступ к свойству управляемого JSF-компонента в рамках сеанса, например:

1
String count = ((CountBean)(Faces.getSessionAttribute("countBean/some_name"))).getCount();

Совершенно законно также делать это (это относится к текущему компоненту):

1
2
3
4
@ManagedBean(name="some_name")
...
String bean_name = getClass().getAnnotation(ManagedBean.class).name();
int count = ((CountBean)(Faces.getSessionAttribute(bean_name))).getCount();

Теперь вы можете легко понять, как работать с управляемыми bean-компонентами, хранящимися в карте сеанса.

Использование @PostConstruct

Как правило, в управляемом bean-компоненте нам нужно написать метод, аннотированный @PostConstruct для выполнения задач инициализации на основе введенных артефактов. Другими словами, аннотация @PostConstruct используется для метода, который должен быть выполнен после внедрения зависимости для выполнения любой инициализации. Когда инициализация не включает в себя введенные артефакты, конструктор может использоваться для инициализации. Для bean- @PostConstruct действия метод, аннотированный @PostConstruct будет вызываться только один раз после создания экземпляра bean- @PostConstruct области действия.

Пример управляемого компонента JSF:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
 
@ManagedBean
@SessionScoped
public class InitBean implements Serializable{
 
 private int init;
 
 public InitBean() {
  init = 5;
 }
 
 public int getInit() {
  return init;
 }
 
 public void setInit(int init) {
  this.init = init;
 }
}
 
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
 
@ManagedBean
@SessionScoped
public class CountBean implements Serializable {
    
 @ManagedProperty("#{initBean}")
 private InitBean initBean;
 
 @PostConstruct
 public void init(){
  LOG.info("CountBean#Initializing counter with @PostConstruct ...");
  count = initBean.getInit();
 }
 
 public void setInitBean(InitBean initBean) {
  this.initBean = initBean;
 }
 ...
}

Пример управляемого компонента CDI:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
 
@Named
@SessionScoped
public class InitBean implements Serializable {
 
 private int init;
 
 public InitBean() {
  init = 5;
 }
 
 public int getInit() {
  return init;
 }
 
 public void setInit(int init) {
  this.init = init;
 }
}
 
import java.io.Serializable;
import javax.inject.Inject;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
 
@Named
@SessionScoped
public class CountBean implements Serializable {
    
 @Inject
 private InitBean initBean;
 
 @PostConstruct
 public void init(){
  LOG.info("CountBean#Initializing counter with @PostConstruct ...");
  count = initBean.getInit();
 }
 ...
}

Инъекционные и сессионные бобы

JSF : Для управляемых компонентов JSF внедрение осуществляется через @ManagedProperty . Например:

инъекция JSF 1 сеанс

инъекция JSF 2 сеанс

CDI : для управляемых компонентов CDI внедрение осуществляется через @Named . Например:

инъекция CDI 1 сеанс

инъекция CDI 2 сеанс

Смешанный JSF и CDI: CDI может быть введен в JSF (наоборот, это не так!)

Сессия JSF и CDI

JSF Managed Beans Ограничения:

! Как правило, в JSF не используйте объекты с более коротким сроком службы, чем объекты, из которых вы их вызываете. Другими словами, используйте объекты, у которых продолжительность жизни такая же или больше, чем у объекта, который вводится. Нарушение этого правила приведет к исключению JSF. Основываясь на этом правиле, в управляемом bean-объекте области действия JSF можно вводить управляемые bean-компоненты сеанса и приложения, но не запрашивать или просматривать управляемые bean-компоненты. Управляемые компоненты JSF могут быть внедрены в другие управляемые компоненты JSF.

Ограничения CDI Managed Beans:

! Когда вы используете объект с более коротким сроком службы, чем у объекта, из которого вы его вызываете (например, вставка bean-объекта с областью запроса в bean-объект сессионного объекта), CDI классифицирует случай использования как несоответствующее внедрение и исправляет проблему через CDI прокси. Для каждого запроса прокси-сервер CDI повторно устанавливает соединение с действующим экземпляром bean-объекта области действия запроса. Управляемые компоненты CDI могут быть внедрены в управляемые компоненты JSF.

Конфигурирование управляемых bean-компонентов JSF-сессии программным способом

Начиная с JSF 2.2, мы можем программно воспроизводить содержимое faces-config.xml . Для управляемых bean-компонентов сессионной области соответствующий фрагмент кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public void populateApplicationConfiguration (Document toPopulate) {
 
 String ns = toPopulate.getDocumentElement().getNamespaceURI();
 
 Element managedbeanEl = toPopulate.createElementNS(ns, "managed-bean");
 
 Element managedbeannameEl = toPopulate.createElementNS(ns, "managed-bean-name");
 managedbeannameEl.appendChild(toPopulate.createTextNode("countBean"));
 managedbeanEl.appendChild(managedbeannameEl);
 
 Element managedbeanclassEl = toPopulate.createElementNS(ns, "managed-bean-class");
 managedbeanclassEl.appendChild(toPopulate.createTextNode("beans.CountBean"));
 managedbeanEl.appendChild(managedbeanclassEl);
 
 Element managedbeanscopeEl = toPopulate. createElementNS(ns, "managed-bean-scope");
 managedbeanscopeEl.appendChild(toPopulate. createTextNode("session"));
 managedbeanEl.appendChild(managedbeanscopeEl);
 ...
 // programmatic create managed-property
 ...
 toPopulate.getDocumentElement().appendChild(managedbeanEl);
}

Полное приложение можно увидеть в книге Mastering JSF 2.2 .

Конфигурирование управляемых bean-объектов JSF-запроса в XML-файле

В конфигурации XML вы можете использовать старый механизм JSF 1.x для определения управляемого компонента в обычном файле faces-config.xml . Например:

01
02
03
04
05
06
07
08
09
10
...
<managed-bean>
 <managed-bean-name>countBean</managed-bean-name>
 <managed-bean-class>beans.CountBean</managed-bean-class>
 <managed-bean-scope>session</managed-bean-scope>
 ...
 <!-- managed bean properties --> via <managed-property/>
 ...
</managed-bean>
...

Управляемые bean-компоненты должны быть определены в отдельном XML-файле, так как faces-config.xml используется для настройки конфигурации уровня приложения. По сути, если вы предпочитаете такой подход, создайте новый XML-файл и поместите в него детали управляемых компонентов. Наконец, объявите файл XML с помощью параметра контекста javax.faces.CONFIG_FILES в файле web.xml .

1
2
3
4
5
6
...
<context-param>
 <param-name>javax.faces.CONFIG_FILES</param-name>
 <param-value>WEB-INF/my-manage-beans.xml</param-value>
</context-param>
...

Увидимся в следующем посте о сфере применения JSF / CDI.

Ссылка: Учебное пособие по JSF — Session Scope Scope от нашего партнера JCG Ангела Леонарда в блоге JSF и поклонников OmniFaces .