我正在阅读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.";
}
}
}
非常感谢任何帮助。谢谢。
答案 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。