JSF何时创建会话,并将其放入会话映射中?

Shi*_*han 1 httpsession jsf-2 mojarra

我正在运行Mojarra 2.2.0。

  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>
Run Code Online (Sandbox Code Playgroud)

托管bean操作方法是-

public void action() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance()
                .getExternalContext().getSession(false);
        System.out.println(session.getId()); // not null for stateful views
    }
Run Code Online (Sandbox Code Playgroud)

对于stateless视图session.getId()抛出NPE

对于非无状态视图-触发GET请求,有 JSESSIONID=340041C96D5AA446D761C3602F54A76D

我读了它在这里觉得─

对于客户端状态保存机制,JSF不会创建会话,并且会在必要时将视图状态存储在表单形式为javax.faces.ViewState的隐藏输入字段中。

此外,这里提到

JSF确实会自动创建会话,因为必须将JSF视图状态存储在该位置。如果将JSF状态保存方法设置为客户端而不是服务器,则它将不会存储在会话中,因此无需创建会话

我认为以上内容为我带来麻烦。

如果将JSF状态保存方法设置为客户端而不是服务器,那么它将不会存储在会话中//完全同意

因此无需创建会话。//这很混乱,因为对于客户端保存机制,会话ID由Servlet容器生成,因此存在与请求相关联的会话。

参考与BalusC在这个问题上的讨论,我创建了一个HttpSessionListener-

@WebListener
public class MyHttpSessionListener implements HttpSessionListener {

    public void sessionCreated(HttpSessionEvent event) {
        Thread.dumpStack();
    }

    public void sessionDestroyed(HttpSessionEvent event) {

    }

}
Run Code Online (Sandbox Code Playgroud)

请参阅以下附件的屏幕截图(这2个屏幕截图适用于2.0.3版,必须存在一个旧的bug才能创建会话)-

在此处输入图片说明

在此处输入图片说明

天秤座(Mojarra 2.2.0)- 在此处输入图片说明

Bal*_*usC 5

JSF何时创建会话

最简单的方法是创建一个HttpSessionListener,在sessionCreated()方法上放置一个调试断点,并检查需要首次获取会话的调用堆栈(因此暗中需要创建该会话)。

在下面的示例中,您将getSession()在调用堆栈中看到一系列调用。您将看到该FaceletViewHandlingStrategy.renderView()方法是第一次调用该方法。

在此处输入图片说明

FaceletViewHandlingStrategy.renderView()在调试器的调用堆栈中单击行后,您将进入其源代码(Maven将自动加载源代码,否则需要手动附加它)。

在此处输入图片说明

您会看到,当启用服务器端状态保存并且呈现的视图不是瞬态的(无状态)时,JSF将隐式创建会话,只是为了确保按时创建会话以保存视图(如果会话是稍后创建的) ,例如在渲染响应阶段,否则您将冒这样的异常风险。添加<h:form>会导致java.lang.IllegalStateException:提交响应后无法创建会话)。

您还将在源代码中立即看到,将状态保存方法设置为client时,或者当视图为in时为无状态时<f:view transient="true">,JSF将不再隐式创建会话。如您所想,较旧的JSF版本可能会这样做,但这应视为错误,并且应在较新的版本中修复。

如果您想确保无状态并避免意外/无法预料的会话创建,则可以throw new IllegalStateException()sessionCreated()方法内部。发生这种情况时,您只需要查看负责创建会话的调用堆栈,然后修复/更改代码就可以了。


它在会话映射中放了什么?

在幕后,ExternalContext#getSessionMap()代表出席HttpSession#setAttribute()/ getAttribute()/ removeAttribute()。您可以使用收听这些方法HttpSessionAttributeListener

在下面的示例中,您将看到该ViewScopeContextManager.getContextMap()行调用SessionMap#put()方法以便将某些内容放入会话映射中。展开event参数时,您将看到会话属性名称和值,分别为com.sun.faces.application.view.activeViewContexts和为空ConcurrentHashMap

在此处输入图片说明

确实,我使用的@Named @ViewScoped是特定页面上的值表达式所引用的a(您会在调用堆栈的下方看到EL解析器和Weld解析器)。当您单击ViewScopeContextManager.getContextMap()调用堆栈中的行时,您会看到它只是在会话范围内准备一个映射以存储视图范围的bean。

在此处输入图片说明

那只是一个例子。会话中可以存储更多内容。以这种方式使用调试器并检查相关的源代码将充分说明Why