Why JSF calls getters multiple times
Asked Answered
R

9

272

Let's say I specify an outputText component like this:

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

If I print a log message when the getter for someProperty is called and load the page, it is trivial to notice that the getter is being called more than once per request (twice or three times is what happened in my case):

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

If the value of someProperty is expensive to calculate, this can potentially be a problem.

I googled a bit and figured this is a known issue. One workaround was to include a check and see if it had already been calculated:

private String someProperty;

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

The main problem with this is that you get loads of boilerplate code, not to mention private variables that you might not need.

What are the alternatives to this approach? Is there a way to achieve this without so much unnecessary code? Is there a way to stop JSF from behaving in this way?

Thanks for your input!

Roue answered 18/1, 2010 at 23:43 Comment(0)
N
359

This is caused by the nature of deferred expressions #{} (note that "legacy" standard expressions ${} behave exactly the same when Facelets is used instead of JSP). The deferred expression is not immediately evaluated, but created as a ValueExpression object and the getter method behind the expression is executed everytime when the code calls ValueExpression#getValue().

This will normally be invoked one or two times per JSF request-response cycle, depending on whether the component is an input or output component (learn it here). However, this count can get up (much) higher when used in iterating JSF components (such as <h:dataTable> and <ui:repeat>), or here and there in a boolean expression like the rendered attribute. JSF (specifically, EL) won't cache the evaluated result of the EL expression at all as it may return different values on each call (for example, when it's dependent on the currently iterated datatable row).

Evaluating an EL expression and invoking a getter method is a very cheap operation, so you should generally not worry about this at all. However, the story changes when you're performing expensive DB/business logic in the getter method for some reason. This would be re-executed everytime!

Getter methods in JSF backing beans should be designed that way that they solely return the already-prepared property and nothing more, exactly as per the Javabeans specification. They should not do any expensive DB/business logic at all. For that the bean's @PostConstruct and/or (action)listener methods should be used. They are executed only once at some point of request-based JSF lifecycle and that's exactly what you want.

Here is a summary of all different right ways to preset/load a property.

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;
    }

}

Note that you should not use bean's constructor or initialization block for the job because it may be invoked multiple times if you're using a bean management framework which uses proxies, such as CDI.

If there are for you really no other ways, due to some restrictive design requirements, then you should introduce lazy loading inside the getter method. I.e. if the property is null, then load and assign it to the property, else return it.

    public SomeObject getSomeProperty() {
        // If there are really no other ways, introduce lazy loading.
        if (someProperty == null) {
            someProperty = loadSomeProperty();
        }

        return someProperty;
    }

This way the expensive DB/business logic won't unnecessarily be executed on every single getter call.

See also:

Neural answered 18/1, 2010 at 23:51 Comment(17)
You are very much right in saying business logic should not be carried out in getters and setters, but I am aware of that. What I want to know is how to avoid this using as little boilerplate code as possible and whether there is something available that addresses this.Roue
Just don't use getters to do business logic. That's all. Rearrange your code logic. My bet that it's already fixed by just using the constructor, postconstruct or action method the smart way.Neural
-1, disagree strongly. The entire point of the javaBeans spec is to allow properties to be more than just a field value, and "derived properties" that are calculated on the fly are perfectly normal. Worrying about redundant getter calls is nothing but premature optimization.Brinkley
Expect if they do more than returning data as you so clearly stated yourself :)Neural
you could add that lazy initialization in getters is still valid in JSF :)Manes
Not just a JSF concept. If data can reasonably be cached post-calculation/DB lookup, just do it. Using a standard approach to save a bunch of time profiling later on is NOT premature optimisation.Ariel
@Brian: use @ViewScoped instead of @RequestScoped. Or just finetune cache at JPA level.Neural
I still haven't moved to JPA (doing JDBC), got all these legacy things going on... Not sure how to make a seamless transition.Ariel
@BalusC: I just have a quick question since I cant test this momentary (At work atm :D). value="#{myBean.someProperty}", as you said, this will call accessor method getSomeProperty() multiple times. However, if we do this value=#{myBean.someMethod()} (GF EL allow us to do this), and in someMethod() I return someProperty;, does this trick JSF to not invoke someMethod() multiples times or does value expression EL know to treat someMethod().Myrick
(This is a continuation of the above post) The reason I ask is because, sometimes I need to have business logic (sadly DB access) in accessor method. For example, let say that I display a List in dataTable, and for each item I need to query DB base on the values of each item. Plus user can at run time at more items to the dataTable.Myrick
@Harry: It won't change the behaviour. You can however handle any business logic in the getter conditionally by lazy loading and/or by checking the current phase ID by FacesContext#getCurrentPhaseId().Neural
@BalusC: If I have a bean serving different lists on different pages, which I face almost daily, Then initializing those lists in constructor or postconstruct might be an overhead? Any workaround for that? or would you consider my problem, a result of bad design and advise me to use last resort, lazy loading..?Telephonic
Depends on the data which those lists represents. Application wide (static) data? (country lists, etc). Session wide data? (user preferences, etc). Request/view wide data? (requestbased search results, etc).Neural
Hi @BalusC, I got your idea when leaving the getter untouched, but how to pass parameters from view to constructor for pre-doing business logic?Providential
Just adding that the trick with if (someProperty == null) {... is dangerous. The first time the getter is called, the fields were not assigned the values from the xhtml page form. So if the getter code depends on their values, it will calculate the incorrect value for that property, making it non-null, and not changing it after the object properties were set and getter being called again. We probably have to reset that property to null in all related properties setters.Palmerpalmerston
@Palo: "If there are for you really no other ways, due to some restrictive design requirements", you might want to review/lift these requirements then.Neural
@Neural yes, thanks. So the best place for me is to put this kind of calculation in an action listener of a command button. I just checked, that method is called only one time and after the setters for the properties have been called.Palmerpalmerston
F
18

With JSF 2.0 you can attach a listener to a system event

<h:outputText value="#{ManagedBean.someProperty}">
   <f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
</h:outputText>

Alternatively you can enclose the JSF page in an f:view tag

<f:view>
   <f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />

      .. jsf page here...

<f:view>
Flop answered 31/10, 2010 at 1:9 Comment(0)
A
9

I have written an article about how to cache JSF beans getter with Spring AOP.

I create a simple MethodInterceptor which intercepts all methods annotated with a special annotation:

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;
}

}

This interceptor is used in a spring configuration file:

    <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>

Hope it will help!

Alanis answered 5/1, 2011 at 14:2 Comment(0)
P
6

Originally posted in PrimeFaces forum @ http://forum.primefaces.org/viewtopic.php?f=3&t=29546

Recently, I have been obsessed evaluating the performance of my app, tuning JPA queries, replacing dynamic SQL queries with named queries, and just this morning, I recognized that a getter method was more of a HOT SPOT in Java Visual VM than the rest of my code (or majority of my code).

Getter method:

PageNavigationController.getGmapsAutoComplete()

Referenced by ui:include in in index.xhtml

Below, you will see that PageNavigationController.getGmapsAutoComplete() is a HOT SPOT (performance issue) in Java Visual VM. If you look further down, on the screen capture, you will see that getLazyModel(), PrimeFaces lazy datatable getter method, is a hot spot too, only when enduser is doing a lot of 'lazy datatable' type of stuff/operations/tasks in the app. :)

Java Visual VM: showing HOT SPOT

See (original) code below.

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;
}

Referenced by the following in index.xhtml:

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

Solution: since this is a 'getter' method, move code and assign value to gmapsAutoComplete prior to method being called; see code below.

/*
 * 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;
    }
}

Test results: PageNavigationController.getGmapsAutoComplete() is no longer a HOT SPOT in Java Visual VM (doesn't even show up anymore)

Sharing this topic, since many of the expert users have advised junior JSF developers to NOT add code in 'getter' methods. :)

Plier answered 14/4, 2013 at 13:32 Comment(0)
E
4

If you are using CDI, you can use Producers methods. It will be called many times, but the result of first call is cached in scope of the bean and is efficient for getters that are computing or initializing heavy objects! See here, for more info.

Eulogium answered 11/6, 2012 at 11:50 Comment(0)
C
3

You could probably use AOP to create some sort of Aspect that cached the results of our getters for a configurable amount of time. This would prevent you from needing to copy-and-paste boilerplate code in dozens of accessors.

Colyer answered 18/1, 2010 at 23:45 Comment(1)
Is this Spring AOP you are talking about? Would you know where I could find a code snippet or two dealing with Aspects? Reading the whole 6th chapter of the Spring documentation seems like overkill as I'm not using Spring ;)Roue
T
-1

I would also advice using such Framework as Primefaces instead of stock JSF, they address such issues before JSF team e. g in primefaces you can set partial submit. Otherwise BalusC has explained it well.

Tutuila answered 18/1, 2010 at 23:43 Comment(0)
B
-1

If the value of someProperty is expensive to calculate, this can potentially be a problem.

This is what we call a premature optimization. In the rare case that a profiler tells you that the calculation of a property is so extraordinarily expensive that calling it three times rather than once has a significant performance impact, you add caching as you describe. But unless you do something really stupid like factoring primes or accessing a databse in a getter, your code most likely has a dozen worse inefficiencies in places you've never thought about.

Brinkley answered 19/1, 2010 at 0:39 Comment(2)
Hence the question - if someProperty corresponds to something expensive to calculate (or as you put it accessing a database or factoring primes), what is the best way to avoid doing the calculation several times per request and is the solution I listed in the question the best one. If you're not answering the question, comments are a good place to post, no? Also, your post seems to contradict your comment on BalusC's post - in the comments you say that it's fine to do calculations on the fly, and in your post you say that it is stupid. Can I ask where you draw the line?Roue
It's a sliding scale, not a black-and-white issue. Some things are clearly not a problems, e.g. adding a few values, because they take less than a millionth of a second (much less, actually). Some clearly are a problem, like DB or file access, because they can take 10ms or longer - and you definitely need to know these so you can avoid them if possible, not just in getters. But for everything else, the line is where the profiler tells you.Brinkley
W
-2

It still big problem in JSF. Fo example if you have a method isPermittedToBlaBla for security checks and in your view you have rendered="#{bean.isPermittedToBlaBla} then the method will be called multiple times.

The security check could be complicated e.g . LDAP query etc. So you must avoid that with

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

and you must ensure within a session bean this per request.

Ich think JSF must implement here some extensions to avoid multiple calls (e.g annotation @Phase(RENDER_RESPONSE) calle this method only once after RENDER_RESPONSE phase...)

Winsome answered 6/1, 2012 at 13:12 Comment(1)
You could cache the result in the RequestParameterMapKutz

© 2022 - 2024 — McMap. All rights reserved.