JSF范围教程– JSF / CDI会话范围

会话作用域跨越多个HTTP请求-响应周期(理论上是无限的)。

jsf cdi omnifaces和deltaspike范围-会话范围

当您需要每个HTTP请求-响应周期进行一次交互时,请求作用域在任何Web应用程序中都非常有用。 但是,当您需要对属于用户会话的任何HTTP请求-响应周期可见的对象时,则需要一个会话范围 ; 在这种情况下,只要HTTP会话存在,该bean就一直存在。 会话作用域允许您创建对象并将其绑定到会话。 它在会话中涉及此bean的第一个HTTP请求时创建,并在HTTP会话无效时销毁。 请求范围存在于JSF和CDI中,并且以相同的方式起作用。 它可以用于非富AJAX和非AJAX请求。

会话范围注释

JSF :JSF请求范围注释是@SessionScopedjavax.faces.bean.SessionScoped )。 具有此范围的bean应该使用@ManagedBeanjavax.faces.bean.ManagedBean )进行注释。 默认范围是@RequestScope

CDI :CDI请求范围注释为@SessionScopedjavax.enterprise.context.SessionScoped )。 具有此范围的bean应该用@Namedjavax.inject.Named )注释。 对于CDI托管bean( @Named ),默认范围是@Dependent伪作用域。

简单的例子

// 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实例。
  • 会话作用域在转发或重定向时不会丢失对象的状态。 在session被销毁之前,对象的状态一直可用(例如,会话超时,无效等)。

基本上,在向会话范围的bean提交数据时必须注意。 只要当前用户会话,提交的数据将“有效”。 因此,一个好的实践将告诉您不要在会话中存储大量数据,尤其是在内存是关键资源的情况下。

实现可序列化

JSF和CDI托管bean应该声明为Serializableimplements Serializable )。 这是必需的,因为容器可能会将会话数据持久化(序列化)到硬盘上。 这样一来,容器就可以管理重载之类的紧急情况,或者仅与集群中的其他服务器共享数据,或者在服务器重启期间恢复会话。

会话范围编程访问

您可以通过编程方式与会话范围进行交互,如下所示:

  • 访问会话范围图
    // 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();
  • 获取会话范围的属性
    // JSF 2.0 - 2.3
    sessionMap.put(name, value);
    
    // OmniFaces
    Faces.setSessionAttribute(name, value);
  • 删除会话范围的属性
    // JSF 2.0-2.3
    Object value = sessionMap.remove(name);
    
    // OmniFaces
    <T> value = Faces.removeSessionAttribute(name);

在JSF页面中,可以使用隐式对象#{sessionScope} (例如,获取CountBean实例: #{sessionScope.countBean} )。

其中,会话映射将包含在会话范围( @SessionScoped (JSF/CDI ))下声明的托管bean实例。

对于JSF托管Bean(不是CDI托管Bean,在这种情况下,密钥非常复杂),您可以通过它们的名称轻松地标识此类Bean,这些名称在会话图中成为密钥。 因此,您将能够在会话映射中的countBean项下找到该JSF托管Bean的实例。 如果您通过@ManagedBean (name =” some_name “)指定bean名称,则some_name将成为会话映射中的键。 因此,通过会话映射,您可以访问会话范围内的JSF托管bean的属性,如下所示:

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

这样做也是完全合法的(这是指当前的bean):

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

现在,您可以轻松地了解如何使用存储在会话映射中的托管bean。

使用

通常,在托管bean中,我们需要编写一个带有@PostConstruct注释的方法,以基于注入的工件来完成初始化任务。 换句话说,@ @PostConstruct注释用于需要依赖注入完成以执行任何初始化之后需要执行的方法。 当初始化不涉及注入的工件时,可以使用构造函数进行初始化。 对于会话作用域的Bean,在创建会话作用域的Bean实例后,将仅调用一次用@PostConstruct注释的方法。

JSF托管bean示例:

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托管Bean示例:

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();
 }
 ...
}

注入和会话作用域bean

JSF :对于JSF托管的bean,注入是通过@ManagedProperty完成的。 例如:

注射JSF 1次

注入JSF 2会话

CDI :对于CDI管理的bean,注入是通过@Named完成的。 例如:

注射CDI 1次

注射CDI 2次

JSF和CDI混合:可以在JSF中注入CDI(反之亦然!)

JSF和CDI会议

JSF托管Bean限制:

作为JSF中的一般规则,请勿使用寿命比调用它的对象更短的对象。 换句话说,请使用寿命与注入对象相同或更长的对象。 违反此规则将导致JSF异常。 根据此规则,在JSF会话范围内的受管Bean中,您可以注入会话和应用程序受管Bean,但不能请求或查看受管Bean。 可以将JSF托管Bean注入其他JSF托管Bean中。

CDI托管Bean限制:

当使用寿命比调用它的对象更短的对象时(例如,将请求范围的Bean注入会话范围的Bean),CDI将用例分类为不匹配的注入,并通过CDI解决问题代理。 对于每个请求,CDI代理都会重新建立与请求范围的Bean的实时实例的连接。 可以将CDI托管bean注入JSF托管bean中。

以编程方式配置JSF会话范围的托管Bean

从JSF 2.2开始,我们可以以编程方式重现faces-config.xml的内容。 对于会话范围内的受管bean,相关的代码片段为:

@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);
}

完整的应用程序可以在精通JSF 2.2的书中看到。

在XML文件中配置JSF请求范围的托管Bean

通过XML配置,您可以使用旧的JSF 1.x机制在普通的faces-config.xml文件中定义托管bean。 例如:

...
<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文件,并将受管bean的详细信息放入其中。 最后,通过web.xml文件中的javax.faces.CONFIG_FILES上下文参数声明XML文件。

...
<context-param>
 <param-name>javax.faces.CONFIG_FILES</param-name>
 <param-value>WEB-INF/my-manage-beans.xml</param-value>
</context-param>
...

在下一篇关于JSF / CDI应用程序范围的文章中见。

翻译自: https://www.javacodegeeks.com/2015/11/jsf-scopes-tutorial-jsfcdi-session-scope.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值