2013-09-01 6 views
1


Я читаю учебник по Java EE 7. В главе 13.12 приведен пример приложения ajaxguessnumber. Я запускаю пример в Glassfish 4, и все работает нормально. Затем я помещаю System.out.println в bean-конструктор, и я понял, что конструктор вызывается дважды во время начальной загрузки страницы. Почему это так, даже для @SessionScoped bean?
Вот XHTML файлJSF: конструктор Bean, вызываемый дважды при начальной загрузке страницы

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

Вот боб 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; 
    } 

} 

И вот боб 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."; 
     } 
    } 
} 

Любая помощь будет оценена. Спасибо.

ответ

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.

EDIT: Я просто нашел другое объяснение проблемы - here.