Categories
el getter jsf performance

Why JSF calls getters multiple times

268

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!

    357

    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:

    17

    • 5

      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

      Jan 19, 2010 at 0:03

    • 3

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

      Jan 19, 2010 at 0:30

    • 3

      Expect if they do more than returning data as you so clearly stated yourself 🙂

      – BalusC

      Jan 19, 2010 at 3:10


    • 4

      you could add that lazy initialization in getters is still valid in JSF 🙂

      – Bozho

      Jan 19, 2010 at 7:13

    • 2

      @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().

      – BalusC

      May 16, 2011 at 18:53


    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>
    

      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!