Search code examples
jsfelfaceletsmanaged-bean

how is the bean accessed first time?


I am going through the tutorial of Java EE 7 in oracle.

Here is a simple form that gets User name in a input box:

<h:body>
        <h:form>
            <h:graphicImage url="#{resource['images:duke.waving.gif']}"
                            alt="Duke waving his hand"/>
            <h2>Hello, my name is Duke. What's yours?</h2>
            <h:inputText id="username"
                         title="My name is: "
                         value="#{hello.name}"
                         required="true"
                         requiredMessage="Error: A name is required."
                         maxlength="25" />
            <p></p>
            <h:commandButton id="submit" value="Submit" action="response">
            </h:commandButton>
            <h:commandButton id="reset" value="Reset" type="reset">
            </h:commandButton>
        </h:form>

I see that value="#{hello.name}" is used in the code. what does this line do?

This is what is mentioned in the tutorial link:

The web page connects to the Hello managed bean through the Expression Language (EL) value expression #{hello.name}, which retrieves the value of the name property from the managed bean.

Q1) But the first time the form is loaded, there is no name attribute attached to hello bean. so Fetching that should return null correct?

Q2) Secondly, how does the value entered in the input box bind to the hello bean's name attribute?

In fact, after the form is submitted, the page is redirected to "response.xhtml" where the value of hello bean's name attribute is fetched in the same way i.e #{hello.name}.


Solution

  • Short answer:

    • Q1: correct
    • Q2:
      1. On each page rendering elements, bound to #{hello.name} will receive the value, returned by Hello.getName() method.
      2. On each form submit (in case form is valid), Hello.setName(param) will be called with param equals to value of element, bound to #{hello.name}

    Longer answer:

    You have:

    1. Hello.java, the class annotated with @Named and @RequestScoped
    2. <h:inputText value="#{hello.name}" /> inside <h:form> on index.xhtml
    3. #{hello.name} inside <h2> on response.xhtml


    Prerequisites:

    • @Named is a CDI annotation.

    • When application is deployed, the server "registers" Hello.java as a managed bean.

    • @Named could be used as @Named(name="explicitHelloName"), but if argument isn't provided, the bean will be registered as "lower cased class name", so, after your application is deployed, we have hello to be used wherever it needed.

    • @RequestScoped says that data of each Hello instance will "live" only during request-response (see this answer for scoping details).

    • This article greatly describes JSF lifecycle. We are interested in two fazes of it: RENDER_RESPONSE and UPDATE_MODEL_VALUES.


    Let's go step by step:

    I. Browser requests index.xhtml

    FacesServlet's responsibility (because we are mapping all *.xhtml files to Faces Servlet inside web.xml) is to build html and return it as a response.

    Building consists of several phases (look here for details), one of which, RENDER-RESPONSE:

    1. Builds a UIViewRoot
    2. Recursively builds UIViewRoot's children.

    During 2) for children, that has some EL expressions, these expressions being "resolved".

    In your case we have #{hello.name} expression, bound to value of an UIInput (h:inputText is an UIInput).

    <h:inputText> will be rendered as HTML's <input type="text">.

    FacesServlet "understands" that value for this input should be taken from something that #{hello.name} represents. To get the value:

    1. FacesServlet "asking" for hello from container (server)
    2. Container searches for registered hello, finding Hello.java, instantiating it and gives to FacesServlet.
    3. FacesServlet calling for getName() (JavaBean convention to get the "name") of Hello instance and receives the value of Hello's private field name, which after class instantiation is null.
    4. FacesServlet writes <input> without value to response and on index.xhtml you see an empty input.

    II. When you press submit button

    ...and form is valid, there is UPDATE_MODEL phase (more detailed here),.

    For our h:inputText component FacesServlet again searches for hello, finding an instance of Hello and calls setName(value) on it. As <h:commandButtons>'s action attribute is response, browser is about to receive response.xhtml's content (this is not a redirection, see this answer, so, @RequestScoped hello isn't being recreated).


    III. response.xhtml

    ...has <h3>Hello, #{hello.name}</h3>, so, again, FacesServlet must resolve it as described earlier.

    But in current request the Hellohave been instantiated and it's field name was already set (in UPDATE_MODEL_VALUES phase) with the value you entered to input, so, <h3>Hello, YOUR_SUBMITTED_TO_INPUT_TEXT_VALUE</h3> is written to response and displayed in browser.


    More details about expressions can be found here

    Edit

    If you use <h:comandButton action="response?faces-redirect=true"/> on the index.xhtml, then after II there will be a redirect to response.xhtml, and Hello bean become recreated, so, you will see "Hello, " (name will be null)