假设我像这样指定了一个outputText组件:

<h:outputText value="#{ManagedBean.someProperty}"/>

如果我打印一条日志消息,当someProperty的getter被调用并加载页面时,很容易注意到getter在每个请求中被调用了不止一次(在我的情况下发生了两次或三次):

DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property

如果someProperty的值计算起来很昂贵,这可能会成为一个问题。

我谷歌了一下,发现这是一个已知的问题。一个解决办法是包含一个检查,看看它是否已经计算过:

private String someProperty;

public String getSomeProperty() {
    if (this.someProperty == null) {
        this.someProperty = this.calculatePropertyValue();
    }
    return this.someProperty;
}

这样做的主要问题是您会得到大量的样板代码,更不用说您可能不需要的私有变量了。

这种方法的替代方案是什么?有没有一种方法可以在没有这么多不必要的代码的情况下实现这一点?是否有一种方法可以阻止JSF以这种方式行为?

谢谢你的建议!


当前回答

如果someProperty的值为 昂贵的计算,这可以 可能是个问题。

这就是我们所说的过早优化。在极少数情况下,如果分析器告诉您某个属性的计算非常昂贵,以至于调用它三次(而不是一次)会产生显著的性能影响,那么您就可以按照您的描述添加缓存。但是,除非您做了一些非常愚蠢的事情,如分解质数或在getter中访问数据库,否则您的代码很可能在您从未想过的地方存在十几个更糟糕的低效率。

其他回答

如果您正在使用CDI,您可以使用生产者方法。 它将被多次调用,但是第一次调用的结果缓存在bean的作用域中,对于正在计算或初始化沉重对象的getter来说非常有效! 更多信息请看这里。

最初发布于PrimeFaces论坛@ http://forum.primefaces.org/viewtopic.php?f=3&t=29546

最近,我一直痴迷于评估我的应用程序的性能,调优JPA查询,用命名查询替换动态SQL查询,就在今天早上,我意识到getter方法在Java Visual VM中比我的其余代码(或大部分代码)更像一个热点。

Getter方法:

PageNavigationController.getGmapsAutoComplete()

由ui:include在index.xhtml中引用

下面,你会看到pagenavigationcontroller . getmapsautocomplete()在Java Visual VM中是一个热点(性能问题)。如果你再往下看,在屏幕截图上,你会看到getLazyModel(), PrimeFaces惰性数据表getter方法,也是一个热点,只有当最终用户在应用程序中做很多“惰性数据表”类型的东西/操作/任务时。

请参阅下面的(原始)代码。

public Boolean getGmapsAutoComplete() {
    switch (page) {
        case "/orders/pf_Add.xhtml":
        case "/orders/pf_Edit.xhtml":
        case "/orders/pf_EditDriverVehicles.xhtml":
            gmapsAutoComplete = true;
            break;
        default:
            gmapsAutoComplete = false;
            break;
    }
    return gmapsAutoComplete;
}

在index.xhtml中引用:

<h:head>
    <ui:include src="#{pageNavigationController.gmapsAutoComplete ? '/head_gmapsAutoComplete.xhtml' : (pageNavigationController.gmaps ? '/head_gmaps.xhtml' : '/head_default.xhtml')}"/>
</h:head>

解决方案:因为这是一个'getter'方法,移动代码并在方法被调用之前将值赋给gmapsAutoComplete;参见下面的代码。

/*
 * 2013-04-06 moved switch {...} to updateGmapsAutoComplete()
 *            because performance = 115ms (hot spot) while
 *            navigating through web app
 */
public Boolean getGmapsAutoComplete() {
    return gmapsAutoComplete;
}

/*
 * ALWAYS call this method after "page = ..."
 */
private void updateGmapsAutoComplete() {
    switch (page) {
        case "/orders/pf_Add.xhtml":
        case "/orders/pf_Edit.xhtml":
        case "/orders/pf_EditDriverVehicles.xhtml":
            gmapsAutoComplete = true;
            break;
        default:
            gmapsAutoComplete = false;
            break;
    }
}

测试结果:PageNavigationController.getGmapsAutoComplete()不再是Java Visual VM中的热点(甚至不再显示)

分享这个主题,因为许多专家用户建议初级JSF开发人员不要在“getter”方法中添加代码。:)

这仍然是JSF中的一个大问题。例如,如果你有一个isPermittedToBlaBla方法用于安全检查,并且在你的视图中你已经呈现了="#{bean。isPermittedToBlaBla}则该方法将被调用多次。

安全检查可能会很复杂。LDAP查询等。所以你必须避免

Boolean isAllowed = null ... if(isAllowed==null){...} return isAllowed?

您必须在会话bean中确保每个请求都有此功能。

我认为JSF必须在这里实现一些扩展来避免多次调用(例如annotation @Phase(RENDER_RESPONSE)只在RENDER_RESPONSE阶段之后调用此方法一次…)

我还建议使用这样的框架作为Primefaces,而不是库存JSF,它们可以在JSF团队之前解决这些问题,例如在Primefaces中你可以设置部分提交。除此之外,BalusC解释得很好。

如果someProperty的值为 昂贵的计算,这可以 可能是个问题。

这就是我们所说的过早优化。在极少数情况下,如果分析器告诉您某个属性的计算非常昂贵,以至于调用它三次(而不是一次)会产生显著的性能影响,那么您就可以按照您的描述添加缓存。但是,除非您做了一些非常愚蠢的事情,如分解质数或在getter中访问数据库,否则您的代码很可能在您从未想过的地方存在十几个更糟糕的低效率。