Performance 为什么JSF多次调用getter

Performance 为什么JSF多次调用getter,performance,jsf,el,getter,Performance,Jsf,El,Getter,假设我指定了一个outputText组件,如下所示: <h:outputText value="#{ManagedBean.someProperty}"/> 如果计算someProperty的值很昂贵,这可能是一个问题 我在谷歌上搜索了一下,发现这是一个众所周知的问题。一种解决方法是包括检查,看看是否已经计算过: private String someProperty; public String getSomeProperty() { if (this.someProp

假设我指定了一个outputText组件,如下所示:

<h:outputText value="#{ManagedBean.someProperty}"/>
如果计算
someProperty
的值很昂贵,这可能是一个问题

我在谷歌上搜索了一下,发现这是一个众所周知的问题。一种解决方法是包括检查,看看是否已经计算过:

private String someProperty;

public String getSomeProperty() {
    if (this.someProperty == null) {
        this.someProperty = this.calculatePropertyValue();
    }
    return this.someProperty;
}
这样做的主要问题是您会得到大量的样板代码,更不用说您可能不需要的私有变量了

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


谢谢你的意见

您可能可以使用AOP创建某种方面,将getter的结果缓存一段可配置的时间。这将防止您需要在几十个访问器中复制和粘贴样板代码。

这是由延迟表达式的性质造成的
{}
(请注意,“遗留”标准表达式
${}
在使用Facelets而不是JSP时的行为完全相同)。延迟表达式不会立即求值,而是创建为对象,每次代码调用时都会执行表达式背后的getter方法

这通常会在每个JSF请求-响应周期调用一到两次,具体取决于组件是输入组件还是输出组件()。但是,当在迭代JSF组件(如
)中使用时,或者在布尔表达式(如
呈现的
属性)中使用时,此计数可能会增加(更多)。JSF(特别是EL)根本不会缓存EL表达式的计算结果,因为它可能会在每次调用时返回不同的值(例如,当它依赖于当前迭代的datatable行时)

对EL表达式求值并调用getter方法是一个非常便宜的操作,因此您通常不必担心这一点。但是,当您出于某种原因在getter方法中执行昂贵的DB/business逻辑时,情况会发生变化。每次都会被重新执行

JSF支持bean中的Getter方法的设计应确保它们只返回已准备好的属性,仅此而已,完全符合。他们根本不应该做任何昂贵的DB/业务逻辑。为此,应该使用bean的
@PostConstruct
和/或(操作)侦听器方法。它们只在基于请求的JSF生命周期的某个点执行一次,这正是您想要的

这里总结了所有预设/加载属性的正确方法

public class Bean {

    private SomeObject someProperty;

    @PostConstruct
    public void init() {
        // In @PostConstruct (will be invoked immediately after construction and dependency/property injection).
        someProperty = loadSomeProperty();
    }

    public void onload() {
        // Or in GET action method (e.g. <f:viewAction action>).
        someProperty = loadSomeProperty();
    }           

    public void preRender(ComponentSystemEvent event) {
        // Or in some SystemEvent method (e.g. <f:event type="preRenderView">).
        someProperty = loadSomeProperty();
    }           

    public void change(ValueChangeEvent event) {
        // Or in some FacesEvent method (e.g. <h:inputXxx valueChangeListener>).
        someProperty = loadSomeProperty();
    }

    public void ajaxListener(AjaxBehaviorEvent event) {
        // Or in some BehaviorEvent method (e.g. <f:ajax listener>).
        someProperty = loadSomeProperty();
    }

    public void actionListener(ActionEvent event) {
        // Or in some ActionEvent method (e.g. <h:commandXxx actionListener>).
        someProperty = loadSomeProperty();
    }

    public String submit() {
        // Or in POST action method (e.g. <h:commandXxx action>).
        someProperty = loadSomeProperty();
        return "outcome";
    }

    public SomeObject getSomeProperty() {
        // Just keep getter untouched. It isn't intented to do business logic!
        return someProperty;
    }

}
这样,就不会在每个getter调用上不必要地执行昂贵的DB/业务逻辑

另见:
如果someProperty的值为 计算起来很昂贵,这可能 这可能是个问题


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

使用JSF 2.0,你可以将侦听器附加到系统事件

<h:outputText value="#{ManagedBean.someProperty}">
   <f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
</h:outputText>
我已经写了一篇关于如何用SpringAOP缓存JSFbean getter的文章

我创建了一个简单的
MethodInterceptor
,它截取用特殊注释注释的所有方法:

public class CacheAdvice implements MethodInterceptor {

private static Logger logger = LoggerFactory.getLogger(CacheAdvice.class);

@Autowired
private CacheService cacheService;

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {

    String key = methodInvocation.getThis() + methodInvocation.getMethod().getName();

    String thread = Thread.currentThread().getName();

    Object cachedValue = cacheService.getData(thread , key);

    if (cachedValue == null){
        cachedValue = methodInvocation.proceed();
        cacheService.cacheData(thread , key , cachedValue);
        logger.debug("Cache miss " + thread + " " + key);
    }
    else{
        logger.debug("Cached hit " + thread + " " + key);
    }
    return cachedValue;
}


public CacheService getCacheService() {
    return cacheService;
}
public void setCacheService(CacheService cacheService) {
    this.cacheService = cacheService;
}

}
此拦截器用于spring配置文件:

    <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
    <property name="pointcut">
        <bean class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
            <constructor-arg index="0"  name="classAnnotationType" type="java.lang.Class">
                <null/>
            </constructor-arg>
            <constructor-arg index="1" value="com._4dconcept.docAdvance.jsfCache.annotation.Cacheable" name="methodAnnotationType" type="java.lang.Class"/>
        </bean>
    </property>
    <property name="advice">
        <bean class="com._4dconcept.docAdvance.jsfCache.CacheAdvice"/>
    </property>
</bean>


希望这会有帮助

这在JSF中仍然是个大问题。例如,如果您有一个用于安全检查的方法
ispermittedtobla
,并且在您的视图中有
rendered=“#{bean.ispermittedtobla}
,则该方法将被多次调用

安全检查可能很复杂,例如LDAP查询等,因此必须使用

Boolean isAllowed = null ... if(isAllowed==null){...} return isAllowed?
并且您必须在会话bean中确保每个请求都有此权限


我认为JSF必须在这里实现一些扩展以避免多次调用(例如注释
@Phase(RENDER\u RESPONSE)
RENDER\u RESPONSE
Phase…)之后只调用此方法一次

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

最初发布在PrimeFaces论坛@

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

吸气剂法:

PageNavigationController.getGmapsAutoComplete()
ui引用:包含在index.xhtml中

下面,您将看到PageNavigationController.getGMapAutoComplete()是Java Visual VM中的一个热点(性能问题),PrimeFaces lazy datatable getter方法也是一个热点,仅当最终用户在应用程序中执行大量“lazy datatable”类型的内容/操作/任务时才如此。:)

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

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;
}
/*
 * 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;
    }
}
由index.xhtml中的以下内容引用:

<h:head>
    <ui:include src="#{pageNavigationController.gmapsAutoComplete ? '/head_gmapsAutoComplete.xhtml' : (pageNavigationController.gmaps ? '/head_gmaps.xhtml' : '/head_default.xhtml')}"/>
</h:head>
测试结果:PageNavigationController.getGMapAutoComplete()不再是Java Visual VM中的热点(甚至不再显示)


分享这个主题,因为许多专家用户建议初级JSF开发人员不要在“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;
}
<h:head>
    <ui:include src="#{pageNavigationController.gmapsAutoComplete ? '/head_gmapsAutoComplete.xhtml' : (pageNavigationController.gmaps ? '/head_gmaps.xhtml' : '/head_default.xhtml')}"/>
</h:head>
/*
 * 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;
    }
}