JSF:Bean构造函数在初始加载页面时调用了两次

时间:2013-09-01 11:06:44

标签: jsf glassfish cdi glassfish-4 java-ee-7


我正在阅读Java EE 7教程。在第13.12章中,有一个示例应用程序,ajaxguessnumber。我在Glassfish 4中运行示例,一切正常。然后我将System.out.println放在bean构造函数中,我意识到构造函数在初始页面加载期间被调用了两次。为什么会这样,即使是@SessionScoped bean呢? 这是xhtml文件

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <h:head>
        <h:outputStylesheet library="css" name="default.css"/>
        <title>Ajax Guess Number Facelets Application</title>
    </h:head>
    <h:body>
        <h:form id="AjaxGuess">
            <h:graphicImage value="#{resource['images:wave.med.gif']}"
                            alt="Duke waving his hand"/>
            <h2>
                Hi, my name is Duke. I am thinking of a number from
                #{dukesNumberBean.minimum} to #{dukesNumberBean.maximum}.
                Can you guess it?
            </h2>
            <p>
                <h:inputText 
                    id="userNo" 
                    title="Type a number from 0 to 10:"
                    value="#{userNumberBean.userNumber}">
                    <f:validateLongRange
                        minimum="#{dukesNumberBean.minimum}"
                        maximum="#{dukesNumberBean.maximum}"/>
                </h:inputText>

                <h:commandButton id="submit" value="Submit" >
                    <f:ajax execute="userNo" render="outputGroup" />
                </h:commandButton>
            </p>
            <p>
                <h:panelGroup layout="block" id="outputGroup">
                    <h:outputText id="result" style="color:blue"
                                  value="#{userNumberBean.response}" rendered="#{!facesContext.validationFailed}"/>
                    <h:message id="errors1" 
                               showSummary="true" 
                               showDetail="false"
                               style="color: #d20005;
                               font-family: 'New Century Schoolbook', serif;
                               font-style: oblique;
                               text-decoration: overline" 
                               for="userNo"/>
                </h:panelGroup>
            </p>
        </h:form>
    </h:body>
</html>

这是bean DukesNumberBean

package javaeetutorial.ajaxguessnumber;

import java.io.Serializable;
import java.util.Random;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;


@Named
@SessionScoped
public class DukesNumberBean implements Serializable {

    private Integer randomInt = null;
    private long maximum = 10;
    private long minimum = 0;

    public DukesNumberBean() {
        System.out.println("Inside DukesNumberBean constructor");

        Random randomGR = new Random();
        long range = maximum+minimum+1;
        randomInt = (int) (minimum + randomGR.nextDouble()*range);
        System.out.println("Duke's number: " + randomInt);
    }

    public long getMaximum() {
        return (this.maximum);
    }

    public void setMaximum(long maximum) {
        this.maximum = maximum;
    }

    public long getMinimum() {
        return (this.minimum);
    }

    public void setMinimum(long minimum) {
        this.minimum = minimum;
    }

    /**
     * @return the randomInt
     */
    public Integer getRandomInt() {
        return randomInt;
    }

    /**
     * @param randomInt the randomInt to set
     */
    public void setRandomInt(Integer randomInt) {
        this.randomInt = randomInt;
    }

}

这是bean UserNumberBean

    package javaeetutorial.ajaxguessnumber;

import java.io.Serializable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@RequestScoped
public class UserNumberBean implements Serializable {
    @Inject
    DukesNumberBean dukesNumberBean;
    private Integer userNumber = null;
    String response = null;

    public UserNumberBean()
    {
        System.out.println("Inside constructor");
    }

    public void setUserNumber(Integer user_number) {
        userNumber = user_number;
    }

    public Integer getUserNumber() {
        return userNumber;
    }

    public String getResponse() {
        if ((userNumber != null) && (userNumber.compareTo(dukesNumberBean.getRandomInt()) == 0)) {
            return "Yay! You got it!";
        }
        if (userNumber == null) {
            return null;
        } else {
            return "Sorry, " + userNumber + " is incorrect.";
        }
    }
}

非常感谢任何帮助。谢谢。

1 个答案:

答案 0 :(得分:7)

这是因为作用域对象并由CDI使用代理注入。 CDI首先创建对象的代理,它是您的真实对象的子类 - 这是第一次调用构造函数,因为当您实例化子类时,始终会调用父构造函数。然后CDI实例化你的真实对象以便注入它 - 这是你的构造函数第二次被调用。 更好的方法是将初始化登录放在像这样的@PostConstruct方法中。

@PostConstruct
public void init() {      
    Random randomGR = new Random();
    long range = maximum+minimum+1;
    randomInt = (int) (minimum + randomGR.nextDouble()*range);
    System.out.println("Duke's number: " + randomInt);
}

它只会被调用一次 - 当真实对象被实例化时。有关您可以找到here的代理的更多信息。

修改: 我刚刚找到了问题的另一种解释 - here