首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么这个getter被呈现的属性调用了这么多次?

为什么这个getter被呈现的属性调用了这么多次?
EN

Stack Overflow用户
提问于 2010-11-26 06:28:12
回答 2查看 15.1K关注 0票数 20

与前面的示例相关,我尝试监视服务器上的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())我想知道为什么这种行为:)

谢谢

添加了一个示例

豆子

@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);之后,通过单击Back返回到profile_main。现在,如果我想重新加载profile_edit (编辑),我需要多次点击该命令按钮。为什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-11-26 07:51:22

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

设置器调用由@ManagedProperty完成。它基本上做了以下工作:

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

getter调用都是由rendered="#{selector.profilePage == 'some'}"在呈现响应阶段完成的。在UIComponent#encodeAll()中,当它第一次计算false时,将不再执行任何调用。当它对true求值时,将按以下顺序再次对其求值六次:

  1. UIComponent#encodeBegin() -定位component.
  2. Renderer#encodeBegin()开头的渲染器-渲染component.
  3. UIComponent#encodeChildren()的开头-定位component.
  4. Renderer#encodeChildren()的子级的渲染器-渲染子component.
  5. UIComponent#encodeEnd()的子级-定位component.
  6. Renderer#encodeEnd()结尾的渲染器-渲染组件的结尾。

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

的确,这看起来既笨拙又低效。根据spec issue 941的说法,它被认为是JSF的阿基里斯愈合。有人建议删除所有这些重复检查,并坚持在UIComponent#encodeAll()中完成的检查,或者在每个阶段的基础上评估isRendered()。很明显,问题的根源在于EL,而不是During EG discussion,而且使用CDI可以极大地提高性能。因此,没有必要从JSF规范方面解决这个问题。

如果您关心的是,如果托管属性为null或empty,则只应在其设置后检查一次,那么可以考虑将其移动到一个带有@PostConstruct注释的方法中。这样的方法将在bean的构造和所有依赖项注入之后直接调用。

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

另请参阅:

票数 36
EN

Stack Overflow用户

发布于 2012-06-11 20:00:18

您可以使用CDI生成器方法。它将被调用多次,但第一次调用的结果被缓存在bean的作用域中,并且对于计算或初始化重对象的getter是有效的!有关更多信息,请参见here

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4281261

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档