There are lot of materials out there differentiating value
attribute and binding
attribute in JSF.
I'm interested in how both approaches differ from each other. Given:
public class User { private String name; private UICommand link; // Getters and setters omitted. }
<h:form> <h:commandLink binding="#{user.link}" value="#{user.name}" /> </h:form>
It is pretty straight forward what happens when a value
attribute is specified. The getter runs to return the name
property value of the User
bean. The value is printed to HTML output.
But I couldn't understand how binding
works. How does the generated HTML maintain a binding with the link
property of the User
bean?
Below is the relevant part of the generated output after manual beautification and commenting (note that the id j_id_jsp_1847466274_1
was auto-generated and that there are two hidden input widgets). I'm using Sun's JSF RI, version 1.2.
<form action="/TestJSF/main.jsf" enctype="application/x-www-form-urlencoded" id="j_id_jsp_1847466274_1" method="post" name="j_id_jsp_1847466274_1"> <input name="j_id_jsp_1847466274_1" type="hidden" value="j_id_jsp_1847466274_1"> <a href="#" onclick="...">Name</a> <input autocomplete="off" id="javax.faces.ViewState" name="javax.faces.ViewState" type="hidden" value="-908991273579182886:-7278326187282654551"> </form>
Where is the binding
stored here?
A JSF "binding" is a binding of the actual JSF UI component to a property. In over 99 cases out of 100, you would use the "value=" attribute, since it's only the control's backing property value you care about dealing with in the backing bean.
To bind a component instance to a bean property, a component tag's binding attribute uses a value expression.
When a JSF view (Facelets/JSP file) get built/restored, a JSF component tree will be produced. At that moment, the view build time, all binding
attributes are evaluated (along with id
attribtues and taghandlers like JSTL). When the JSF component needs to be created before being added to the component tree, JSF will check if the binding
attribute returns a precreated component (i.e. non-null
) and if so, then use it. If it's not precreated, then JSF will autocreate the component "the usual way" and invoke the setter behind binding
attribute with the autocreated component instance as argument.
In effects, it binds a reference of the component instance in the component tree to a scoped variable. This information is in no way visible in the generated HTML representation of the component itself. This information is in no means relevant to the generated HTML output anyway. When the form is submitted and the view is restored, the JSF component tree is just rebuilt from scratch and all binding
attributes will just be re-evaluated like described in above paragraph. After the component tree is recreated, JSF will restore the JSF view state into the component tree.
Important to know and understand is that the concrete component instances are effectively request scoped. They're newly created on every request and their properties are filled with values from JSF view state during restore view phase. So, if you bind the component to a property of a backing bean, then the backing bean should absolutely not be in a broader scope than the request scope. See also JSF 2.0 specitication chapter 3.1.5:
3.1.5 Component Bindings
...
Component bindings are often used in conjunction with JavaBeans that are dynamically instantiated via the Managed Bean Creation facility (see Section 5.8.1 “VariableResolver and the Default VariableResolver”). It is strongly recommend that application developers place managed beans that are pointed at by component binding expressions in “request” scope. This is because placing it in session or application scope would require thread-safety, since UIComponent instances depends on running inside of a single thread. There are also potentially negative impacts on memory management when placing a component binding in “session” scope.
Otherwise, component instances are shared among multiple requests, possibly resulting in "duplicate component ID" errors and "weird" behaviors because validators, converters and listeners declared in the view are re-attached to the existing component instance from previous request(s). The symptoms are clear: they are executed multiple times, one time more with each request within the same scope as the component is been bound to.
And, under heavy load (i.e. when multiple different HTTP requests (threads) access and manipulate the very same component instance at the same time), you may face sooner or later an application crash with e.g. Stuck thread at UIComponent.popComponentFromEL, or Threads stuck at 100% CPU utilization in HashMap during JSF saveState(), or even some "strange" IndexOutOfBoundsException
or ConcurrentModificationException
coming straight from JSF implementation source code while JSF is busy saving or restoring the view state (i.e. the stack trace indicates saveState()
or restoreState()
methods and like).
Also, as a single component basically references the rest of the entire component tree via getParent()
and getChildren()
, when binding a single component to a view or session scoped bean, you're essentially saving the entire JSF component tree in the HTTP session for nothing. This will get really costly in terms of available server memory when you have relatively a lot of components in the view.
binding
on a bean property is bad practiceRegardless, using binding
this way, binding a whole component instance to a bean property, even on a request scoped bean, is in JSF 2.x a rather rare use case and generally not the best practice. It indicates a design smell. You normally declare components in the view side and bind their runtime attributes like value
, and perhaps others like styleClass
, disabled
, rendered
, etc, to normal bean properties. Then, you just manipulate exactly that bean property you want instead of grabbing the whole component and calling the setter method associated with the attribute.
In cases when a component needs to be "dynamically built" based on a static model, better is to use view build time tags like JSTL, if necessary in a tag file, instead of createComponent()
, new SomeComponent()
, getChildren().add()
and what not. See also How to refactor snippet of old JSP to some JSF equivalent?
Or, if a component needs to be "dynamically rendered" based on a dynamic model, then just use an iterator component (<ui:repeat>
, <h:dataTable>
, etc). See also How to dynamically add JSF components.
Composite components is a completely different story. It's completely legit to bind components inside a <cc:implementation>
to the backing component (i.e. the component identified by <cc:interface componentType>
. See also a.o. Split java.util.Date over two h:inputText fields representing hour and minute with f:convertDateTime and How to implement a dynamic list with a JSF 2.0 Composite Component?
binding
in local scopeHowever, sometimes you'd like to know about the state of a different component from inside a particular component, more than often in use cases related to action/value dependent validation. For that, the binding
attribute can be used, but not in combination with a bean property. You can just specify an in the local EL scope unique variable name in the binding
attribute like so binding="#{foo}"
and the component is during render response elsewhere in the same view directly as UIComponent
reference available by #{foo}
. Here are several related questions where such a solution is been used in the answer:
Validate input as required only if certain command button is pressed
How to render a component only if another component is not rendered?
JSF 2 dataTable row index without dataModel
Primefaces dependent selectOneMenu and required="true"
Validate a group of fields as required when at least one of them is filled
How to change css class for the inputfield and label when validation fails?
Getting JSF-defined component with Javascript
Use an EL expression to pass a component ID to a composite component in JSF
(and that's only from the last month...)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With