没有 @ViewScoped 的 JSF

Stu*_*ion 5 jsf primefaces view-scope

我已经使用 JSF 很多年了,在下一个项目中,我们的目标是使 Web 层尽可能无状态。我正在探索的一种可能性是去除@ViewScoped豆子以支持@RequestScoped@SessionScoped根据需要加上一两个豆子)。这对于带有 AJAX、数据表和条件渲染的复杂页面来说很麻烦。我的问题是:JSF(和 PrimeFaces)与无状态 Web bean 的工作情况如何?这是我应该继续探索的东西,还是@ViewScope现在如此基本以至于不值得付出努力?

我很感激在我写这个问题时它可能会被关闭为“主要基于意见”,但我希望它不是,我对@ViewScope解决的具体问题感兴趣,以及我必须重新解决哪些历史性的解决方法-通过忽略引入@ViewScoped

Bal*_*usC 5

JSF(和 PrimeFaces)与无状态 Web Bean 的配合效果如何?

技术上是可行的。

JSF 使用视图状态主要是为了跟踪组件的“禁用”、“只读”和“渲染”属性UIInput以及UICommand“提交的值”、“本地值”和“有效吗?” 组件的状态EditableValueHolder(由其他组件实现UIInput)。

在“disabled”、“readonly”和“rendered”属性的情况下,如果这些属性表示 EL 表达式,那么 JSF 将在处理表单提交请求期间重新检查它。下面是一个基本示例:

<h:form>
    <h:commandButton value="toggle" action="#{bean.toggle}">
        <f:ajax render="panel" />
    </h:commandButton>
    <h:panelGroup id="panel">
        <h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
            <f:ajax />
        </h:commandButton>
    </h:panelGroup>
</h:form>
Run Code Online (Sandbox Code Playgroud)
@Named
@ViewScoped
public class Bean implements Serializable {

    private static final long serialVersionUID = 1L;

    private boolean toggled;

    public void toggle() {
        this.toggled = !toggled;
    }

    public void submit() {
        System.out.println("Submitted");
    }

    public boolean isToggled() {
        return toggled;
    }
}
Run Code Online (Sandbox Code Playgroud)

首先单击“切换”按钮,然后单击“提交”按钮。如果是视图作用域 bean,它会工作得很好。但是,如果您替换@ViewScoped@RequestScoped此处,那么它将失败,因为toggled默认返回到false此时 JSF 需要在回发请求期间解码“提交”按钮,因此其rendered属性将进行评估false,最终 JSF 不会对操作事件进行排队。

在这种情况下,您需要确保在请求作用域 bean 的(后)构造期间将该属性预先初始化为预期值。一种方法是在 ajax 更新的组件中使用隐藏的输入字段。这是调整后的示例:

<h:form>
    <h:commandButton value="toggle" action="#{bean.toggle}">
        <f:ajax render="panel" />
    </h:commandButton>
    <h:panelGroup id="panel">
        <input type="hidden" name="toggled" value="#{bean.toggled}" />
        <h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
            <f:ajax />
        </h:commandButton>
    </h:panelGroup>
</h:form>
Run Code Online (Sandbox Code Playgroud)
@Named
@RequestScoped
public class Bean {

    @Inject @ManagedProperty("#{param.toggled}")
    private boolean toggled;

    public void toggle() {
        this.toggled = !toggled;
    }

    public void submit() {
        System.out.println("Submitted");
    }

    public boolean isToggled() {
        return toggled;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:<h:inputHidden>不幸的是,a 将不起作用,因为它仅在操作事件排队后才更新模型值。即使没有就可以immediate="true"了。顺便说一句,这让我产生了为<o:inputHidden>OmniFaces 推出新产品的想法。

有了这些改变,它就会工作得很好。

然而,由于最初属于视图范围的状态(toggled属性)现在已成为请求参数,因此它完全暴露于外界,因此也可以被黑客篡改。想要调用“提交”按钮而不首先调用“切换”按钮的黑客现在可以简单地手动添加请求参数toggled=true。这是否可取取决于应用程序的业务需求,但通常情况下这是完全不可取的。

这就是 JSF 试图通过提供将这些敏感属性放入@ViewScopedbean 中的可能性来保护您免受的影响。


这对于使用 AJAX、数据表和条件渲染的复杂页面来说是很麻烦的

确实如此,但在技术上仍然不是不可能。您只需通过手动填充的隐藏输入字段手动进行分页、排序和过滤状态,如上所示。支持<p:dataTable>将这些状态绑定到 bean 属性。例如:

<p:dataTable ...
    first="#{bean.first}"
    sortField="#{bean.sortField}"
    sortOrder="#{bean.sortOrder}"
    filterBy="#{bean.filterBy}">
    ...
</p:dataTable> 
Run Code Online (Sandbox Code Playgroud)

您可以将它们复制到<input type="hidden">前面演示的字段中(您确保已被覆盖<p:ajax update>!),最后通过@ManagedProperty和/或@PostConstruct.

javax.faces.ViewState实际上,您基本上通过这种方式重新发明了当前已经通过隐藏输入字段与@ViewScopedbean结合完成的工作。那么为什么不立即使用它呢?:)

如果您主要关心的是内存使用情况,那么您需要仔细设计您的 bean,以便仅将视图作用域状态存储在 bean 中@ViewScoped,并且将请求作用域状态存储在@RequestScopedbean 中。例如,将数据模型放在请求作用域 bean 中并将分页/排序/过滤状态放在视图作用域 bean 中是完全可以的。您可能还需要考虑OmniFaces@ViewScoped,因为它会在页面卸载时立即销毁视图状态和物理 bean。

也就是说,考虑到这个问题,我几个小时前验证并改进了OptimusFaces库,以确保它也完全支持无状态视图<f:view transient="true">以及新的集成测试。OptimusFaces 的优点之一是您不再需要手动担心分页/排序/过滤状态。OptimusFaces 会为您担心。

也可以看看: