没有 @ViewScoped 的 JSF
JSF without @ViewScoped
多年来我一直在使用 JSF,在下一个项目中,我们的目标是使 Web 层尽可能无状态。我正在探索的一种可能性是删除 @ViewScoped
bean 以支持 @RequestScoped
(根据需要加上一两个 @SessionScoped
bean)。这对于具有 AJAX、数据表和条件呈现的复杂页面来说很麻烦。我的问题是:JSF(和 PrimeFaces)与无状态 Web bean 的配合情况如何?这是我应该继续探索的东西,还是 @ViewScope
现在如此基础以至于不值得付出努力?
当我写这个问题时,我很感激它可能会被关闭为 'primarily opinion based',但我希望它不是,我对 @ViewScope
解决的具体问题很感兴趣以及我必须通过忽略 @ViewScoped
.
重新引入哪些历史性解决方法
How well does JSF (and PrimeFaces) work with stateless web beans?
技术上是可行的。
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>
@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;
}
}
首先单击“切换”按钮,然后单击“提交”按钮。如果是视图范围的 bean,它会工作得很好。但是,如果您在此处将 @ViewScoped
替换为 @RequestScoped
,那么它将失败,因为此时 toggled
默认返回到 false
,此时 JSF 需要解码“提交”按钮postback 请求,因此它的 rendered
属性将评估 false
并且最终 JSF 不会对操作事件进行排队。
在这种情况下,您需要自己确保 属性 在 (post) 请求作用域 bean 的构造期间预初始化为预期值。一种方法是在 ajax-updated 组件中为此使用隐藏的输入字段。这是调整后的示例:
<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>
@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;
}
}
NOTE: a <h:inputHidden>
will unfortunately not work as it updates the model value only after the action event is to be queued. Even not with a immediate="true"
on it. This brings me by the way on the idea for a new <o:inputHidden>
for OmniFaces.
通过这些更改,它会正常工作。
但是,由于原来是视图范围的状态(toggled
属性)现在变成了一个请求参数,它完全暴露给世界,因此也可以被黑客篡改。想要调用“提交”按钮而不首先调用“切换”按钮的黑客现在可以简单地手动添加请求参数 toggled=true
。这是否可取取决于您的应用程序的业务需求,但通常情况下这是完全不可取的。
这就是 JSF 试图通过提供将这些敏感属性放入 @ViewScoped
bean 中的可能性来保护您免受的影响。
This is proving troublesome for complex pages with AJAX, datatables and conditional rendering
是的,但在技术上仍然不是不可能的。您只需通过手动填充的隐藏输入字段手动携带分页、排序和过滤状态,如上所示。 <p:dataTable>
支持将这些状态绑定到 bean 属性。例如:
<p:dataTable ...
first="#{bean.first}"
sortField="#{bean.sortField}"
sortOrder="#{bean.sortOrder}"
filterBy="#{bean.filterBy}">
...
</p:dataTable>
您可以像之前演示的那样将它们复制到 <input type="hidden">
字段中(您确保已被 <p:ajax update>
覆盖!),最后通过 @ManagedProperty
and/or 获取它们@PostConstruct
.
实际上,您基本上是通过这种方式重新发明目前已经由 javax.faces.ViewState
隐藏输入字段与 @ViewScoped
beans 结合完成的工作。那么为什么不立即使用它呢? :)
如果您主要关心的是内存使用,那么您需要仔细设计 bean,以便 仅 视图范围状态存储在 @ViewScoped
bean 并且 只有 请求范围内的状态存储在 @RequestScoped
bean 中。例如,将数据模型放在请求范围的 bean 中,将 paginated/sorted/filtered 状态放在视图范围的 bean 中是完全没问题的。您可能还想考虑 OmniFaces @ViewScoped
,因为它会在页面卸载时立即销毁视图状态和物理 bean。
也就是说,考虑到这个问题,我已经 just a few hours ago verified and improved the OptimusFaces 库确保它也完全支持 <f:view transient="true">
的无状态视图,以及一个新的集成测试。 OptimusFaces 的优势之一是您不再需要手动担心携带 paginated/sorted/filtered 状态。 OptimusFaces 会为你操心
另请参阅:
- Why JSF saves the state of UI components on server?
- How to choose the right bean scope?
- What is the usefulness of statelessness in JSF?
多年来我一直在使用 JSF,在下一个项目中,我们的目标是使 Web 层尽可能无状态。我正在探索的一种可能性是删除 @ViewScoped
bean 以支持 @RequestScoped
(根据需要加上一两个 @SessionScoped
bean)。这对于具有 AJAX、数据表和条件呈现的复杂页面来说很麻烦。我的问题是:JSF(和 PrimeFaces)与无状态 Web bean 的配合情况如何?这是我应该继续探索的东西,还是 @ViewScope
现在如此基础以至于不值得付出努力?
当我写这个问题时,我很感激它可能会被关闭为 'primarily opinion based',但我希望它不是,我对 @ViewScope
解决的具体问题很感兴趣以及我必须通过忽略 @ViewScoped
.
How well does JSF (and PrimeFaces) work with stateless web beans?
技术上是可行的。
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>
@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;
}
}
首先单击“切换”按钮,然后单击“提交”按钮。如果是视图范围的 bean,它会工作得很好。但是,如果您在此处将 @ViewScoped
替换为 @RequestScoped
,那么它将失败,因为此时 toggled
默认返回到 false
,此时 JSF 需要解码“提交”按钮postback 请求,因此它的 rendered
属性将评估 false
并且最终 JSF 不会对操作事件进行排队。
在这种情况下,您需要自己确保 属性 在 (post) 请求作用域 bean 的构造期间预初始化为预期值。一种方法是在 ajax-updated 组件中为此使用隐藏的输入字段。这是调整后的示例:
<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>
@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;
}
}
NOTE: a
<h:inputHidden>
will unfortunately not work as it updates the model value only after the action event is to be queued. Even not with aimmediate="true"
on it. This brings me by the way on the idea for a new<o:inputHidden>
for OmniFaces.
通过这些更改,它会正常工作。
但是,由于原来是视图范围的状态(toggled
属性)现在变成了一个请求参数,它完全暴露给世界,因此也可以被黑客篡改。想要调用“提交”按钮而不首先调用“切换”按钮的黑客现在可以简单地手动添加请求参数 toggled=true
。这是否可取取决于您的应用程序的业务需求,但通常情况下这是完全不可取的。
这就是 JSF 试图通过提供将这些敏感属性放入 @ViewScoped
bean 中的可能性来保护您免受的影响。
This is proving troublesome for complex pages with AJAX, datatables and conditional rendering
是的,但在技术上仍然不是不可能的。您只需通过手动填充的隐藏输入字段手动携带分页、排序和过滤状态,如上所示。 <p:dataTable>
支持将这些状态绑定到 bean 属性。例如:
<p:dataTable ...
first="#{bean.first}"
sortField="#{bean.sortField}"
sortOrder="#{bean.sortOrder}"
filterBy="#{bean.filterBy}">
...
</p:dataTable>
您可以像之前演示的那样将它们复制到 <input type="hidden">
字段中(您确保已被 <p:ajax update>
覆盖!),最后通过 @ManagedProperty
and/or 获取它们@PostConstruct
.
实际上,您基本上是通过这种方式重新发明目前已经由 javax.faces.ViewState
隐藏输入字段与 @ViewScoped
beans 结合完成的工作。那么为什么不立即使用它呢? :)
如果您主要关心的是内存使用,那么您需要仔细设计 bean,以便 仅 视图范围状态存储在 @ViewScoped
bean 并且 只有 请求范围内的状态存储在 @RequestScoped
bean 中。例如,将数据模型放在请求范围的 bean 中,将 paginated/sorted/filtered 状态放在视图范围的 bean 中是完全没问题的。您可能还想考虑 OmniFaces @ViewScoped
,因为它会在页面卸载时立即销毁视图状态和物理 bean。
也就是说,考虑到这个问题,我已经 just a few hours ago verified and improved the OptimusFaces 库确保它也完全支持 <f:view transient="true">
的无状态视图,以及一个新的集成测试。 OptimusFaces 的优势之一是您不再需要手动担心携带 paginated/sorted/filtered 状态。 OptimusFaces 会为你操心
另请参阅:
- Why JSF saves the state of UI components on server?
- How to choose the right bean scope?
- What is the usefulness of statelessness in JSF?