tags:

views:

1304

answers:

4

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!

+1  A: 

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.

matt b
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 ;)
Sevas
+8  A: 

Getters are solely there to access bean properties, not to do some business logic. There you have the bean constructor, initialization blocks or event methods for. All are executed only once during bean's life and that's exactly what you want.

I've summarized all different right ways to preset/load a property.

public class Bean {

    // Direct initialization.
    private SomeObject someProperty = loadSomeProperty();

    {
        // Or in init block (applies to all constructors, not so relevant in javabeans).
        this.someProperty = loadSomeProperty();
    }

    public Bean() {
        // Or in constructor.
        this.someProperty = loadSomeProperty();
    }

    @PostConstruct
    public void init() {
        // Or in @PostConstruct (Will be invoked AFTER construction and dependency/property injection).
        this.someProperty = loadSomeProperty();
    }

    public void change(ValueChangeEvent event) {
        // Or in some FacesEvent method.
        this.someProperty = loadSomeProperty();
    }

    public String submit() {
        // Or in Action method.
        this.someProperty = loadSomeProperty();
        return "outcome";
    }

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

}

A getter will normally be called one or two times per JSF request-response cycle (learn it here), but it can get up (much) higher, especially when used in UIData components or here and there in a boolean expression like the rendered attribute. Once again, don't use a getter for other purposes than just returning the data.

BalusC
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.
Sevas
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.
BalusC
@Downvoter: you clearly know nothing about JSF.
BalusC
-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.
Michael Borgwardt
Expect if they do more than returning data as you so clearly stated yourself :)
BalusC
you could add that lazy initialization in getters is still valid in JSF :)
Bozho
Yes, as last resort. But the OP did find it a tedious work.
BalusC
There's a world of difference (about 5 orders of magnitude) between DB/file access and doing simple calculations. Strictly forbidding anything except returning field values means you clearly know nothing about computing.
Michael Borgwardt
We both clearly have a misunderstanding :) No worries further.
BalusC
A: 

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.

Michael Borgwardt
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?
Sevas
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.
Michael Borgwardt
Point taken, cheers.
Sevas
A: 

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>
César L