【发布时间】:2011-01-04 14:49:00
【问题描述】:
我有两个用于同一个会话的 SessionScoped CDI bean 实例。我的印象是 CDI 会为我生成一个实例,但它生成了两个。是我误解了 CDI 的工作原理,还是发现了错误?
这是 bean 代码:
package org.mycompany.myproject.session;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import javax.servlet.http.HttpSession;
@Named @SessionScoped public class MyBean implements Serializable {
private String myField = null;
public MyBean() {
System.out.println("MyBean constructor called");
FacesContext fc = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)fc.getExternalContext().getSession(false);
String sessionId = session.getId();
System.out.println("Session ID: " + sessionId);
}
public String getMyField() {
return myField;
}
public void setMyField(String myField) {
this.myField = myField;
}
}
这是 Facelet 代码:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view contentType="text/html" encoding="UTF-8">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form id="form">
<h:inputText value="#{myBean.myField}"/>
<h:commandButton value="Submit"/>
</h:form>
</h:body>
</f:view>
</html>
这是部署和导航到页面的输出:
INFO: Loading application org.mycompany_myproject_war_1.0-SNAPSHOT at /myproject
INFO: org.mycompany_myproject_war_1.0-SNAPSHOT was successfully deployed in 8,237 milliseconds.
INFO: MyBean constructor called
INFO: Session ID: 175355b0e10fe1d0778238bf4634
INFO: MyBean constructor called
INFO: Session ID: 175355b0e10fe1d0778238bf4634
使用 GlassFish 3.0.1
【问题讨论】:
-
我实际上被一个相关的问题警告过:在构造函数(或初始化程序块)中调用非最终方法会导致 CDI 出现意外影响。我从那以后读到不建议使用非最终方法(download.oracle.com/javase/tutorial/java/javaOO/initial.html)。如果我使用非最终方法来初始化 CDI bean 中的列表,那么初始化器会被调用两次!注意:CDI 不允许使用 final 方法,并且会抛出运行时异常,说明 bean 不可代理。 “修复”是不调用非最终方法并在初始化程序块中完成所有工作。
-
我注意到,如果我定义了一个用 @PostConstruct 注释的 init 方法,它只会被调用一次(尽管创建了两个 bean 实例)。我猜 CDI 正在创建我的 bean 实例池,并在将它们从池中拉出时调用 post 构造。我猜想将仍在池中的 bean 实例与当前 HTTP 会话相关联是没有意义的。
-
在下面进一步查看我的回复。 2 个实例第一个是上下文实例,第二个是代理。 @PostConstruct 当然只会被上下文实例调用,not 被代理调用。