gpt4 book ai didi

jsf - 为什么 getter 被渲染属性调用了这么多次?

转载 作者:行者123 更新时间:2023-12-03 13:21:48 25 4
gpt4 key购买 nike

与前面的示例相关,我尝试监视服务器上的 get/set 方法(调用它们的时间和频率)。所以,我实际看起来是这样的:

@ManagedBean(name="selector")
@RequestScoped
public class Selector {
@ManagedProperty(value="#{param.profilePage}")
private String profilePage;

public String getProfilePage() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}

System.out.println("GET "+profilePage);

return profilePage;
}
public void setProfilePage(String profilePage) {
this.profilePage=profilePage;
System.out.println("SET "+profilePage);
}
}

唯一可以调用此方法的页面(它只在渲染时调用 get 方法)是:
<!DOCTYPE html>
<ui:composition
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">

<h:panelGroup layout="block" id="profileContent">
<h:panelGroup rendered="#{selector.profilePage=='main'}">
// nothing at the moment
</h:panelGroup>
</h:panelGroup>
</ui:composition>

当我看到服务器日志时,我傻了,我看到:
SET null
GET main
GET main
GET main
GET main
GET main
GET main
GET main

什么?它调用了 getProfilePage() 的七次方法? (还有 1 次 setProfilePage() )
我想知道为什么这种行为:)

谢谢

添加了一个示例

bean
@ManagedBean(name="selector")
@RequestScoped
public class Selector {
@ManagedProperty(value="#{param.profilePage}")
private String profilePage;

@PostConstruct
public void init() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}
}

public String getProfilePage() { return profilePage; }
public void setProfilePage(String profilePage) { this.profilePage=profilePage; }
}
profile.xhtml
<h:panelGroup layout="block" id="profileContent">
<h:panelGroup layout="block" styleClass="content_title">
Profilo Utente
</h:panelGroup>

<h:panelGroup rendered="#{selector.profilePage=='main'}">
<ui:include src="/profile/profile_main.xhtml" />
</h:panelGroup>

<h:panelGroup rendered="#{selector.profilePage=='edit'}">
<ui:include src="/profile/profile_edit.xhtml" />
</h:panelGroup>
</h:panelGroup>

// profile_main.xhtml
<h:form id="formProfileMain" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />

<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="EDIT">
<f:setPropertyActionListener target="#{selector.profilePage}" value="edit" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>

// profile_edit.xhtml
<h:form id="formProfileEdit" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />

<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="Edit">
<f:setPropertyActionListener target="#{selector.profilePage}" value="editProfile" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>

<h:commandButton value="Back">
<f:setPropertyActionListener target="#{selector.profilePage}" value="main" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>

在这个例子中,我调用 profile_main (默认);在(例如)我调用 profile_edit 之后(通过单击 EDIT);之后,我通过单击返回返回 profile_main。现在,如果我想重新加载 profile_edit(编辑),我需要多次单击该命令按钮。为什么?

最佳答案

EL(表达式语言,那些 #{} 的东西)不会缓存调用的结果。它只是直接访问 bean 中的数据。如果 getter 只是 ,这通常不会造成伤害。返回 数据。

setter 调用由 @ManagedProperty 完成.它基本上做了以下工作:

selector.setProfilePage(request.getParameter("profilePage"));

getter 调用全部由 rendered="#{selector.profilePage == 'some'}" 完成。在渲染响应阶段。当它评估 false第一次,在 UIComponent#encodeAll() ,那么将不再进行调用。当它评估 true ,然后将按以下顺序再评估六次:
  • UIComponent#encodeBegin() - 定位组件开头的渲染器。
  • Renderer#encodeBegin() - 渲染组件的开始。
  • UIComponent#encodeChildren() - 定位组件子级的渲染器。
  • Renderer#encodeChildren() - 渲染组件的子组件。
  • UIComponent#encodeEnd() - 定位组件末尾的渲染器。
  • Renderer#encodeEnd() - 渲染组件的结尾。

  • 组件及其渲染器在每个步骤中验证是否允许渲染。在表单提交期间,如果输入或命令组件或其任何父组件具有 rendered属性,那么它也将在应用请求值阶段进行评估,作为防止篡改/黑客请求的一部分。

    诚然,这看起来很笨拙且效率低下。根据 spec issue 941,它被认为是 JSF 的致命弱点。 .建议删除所有这些重复检查并坚持 UIComponent#encodeAll() 中所做的检查。 , 或评估 isRendered()在每个阶段的基础上。 During EG discussion ,很明显问题的根源在于 EL,而不是 JSF,而使用 CDI 可以大大提高性能。所以没有必要从 JSF 规范方面解决它。

    如果您担心托管属性应在其设置后仅检查一次(如果它为 null 或空),则考虑将其移至使用 @PostConstruct 注释的方法中。 .这样的方法会在bean的构建和所有的依赖注入(inject)之后直接调用。
    @PostConstruct
    public void init() {
    if (profilePage == null || profilePage.trim().isEmpty()) {
    profilePage = "main";
    }
    }

    也可以看看:
  • Why JSF calls getters multiple times?
  • 关于jsf - 为什么 getter 被渲染属性调用了这么多次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4281261/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com