为什么 GlassFish 5.1.0 中的 JSF 将我的 @ViewScoped CDI bean 的 ID 属性 设置为空?
Why is JSF in GlassFish 5.1.0 setting the ID property of my @ViewScoped CDI bean to null?
我在从 GlassFish 4.1.2 升级到 GlassFish 5.1.0 的过程中遇到了一个问题,即 JSF 生命周期的更新模型阶段正在设置我的 @ 的 ID 属性 的值ViewScoped CDI 支持 beans 为 null。以下是我的简化视图。
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/templates/modena.xhtml">
<ui:define name="title">Location Editor2</ui:define>
<ui:define name="content">
<f:metadata>
<f:viewParam name="id" value="#{locationEditor2.location.id}" />
<f:viewAction action="#{locationEditor2.setLocationById}" />
</f:metadata>
<h:form id="location-editor-form">
<p:commandButton value="Update" styleClass="RaisedButton"
action="#{locationEditor2.updateLocation}" update="@form" />
</h:form>
</ui:define>
</ui:composition>
以下是我简化的 CDI backing bean。
import java.io.Serializable;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.elliottlogic.elis.ejb.location.Location;
import com.elliottlogic.elis.ejb.location.LocationManager;
import com.elliottlogic.elis.ejb.security.AuthorizationException;
import com.elliottlogic.elis.wa.core.ExceptionTools;
/**
* The <code>LocationEditor2</code> class TODO Complete Type Description
*
* @author jre
* @version $Id: $
*
*/
@Named
@ViewScoped
public class LocationEditor2 implements Serializable {
final static Logger logger = LoggerFactory.getLogger(LocationEditor2.class.getName());
static final long serialVersionUID = 1L;
private Location location = new Location();
@EJB
private LocationManager locationManager;
/**
* Configures model using persisted values.
*
* @throws AuthorizationException
*/
public void setLocationById() throws AuthorizationException {
try {
location = locationManager.readLocationByID(location.getId());
} catch (EJBException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Location not found.", "Location not found."));
ExceptionTools.unwrapEJBException(FacesContext.getCurrentInstance(), e);
}
}
/**
* Saves the changes to the location.
*
* @return
*/
public String updateLocation() {
logger.debug("Location ID: {}", location.getId());
return null;
}
/**
* @return the location
*/
public Location getLocation() {
return location;
}
/**
* @param location
* the location to set
*/
public void setLocation(Location location) {
this.location = location;
}
}
下面演示了这个问题:
- 将浏览器指向 LocationEditor2.xhtml?id=1
- Select [更新] 按钮。
- updateLocation 方法记录 "Location ID: 1"
- Select [更新] 按钮
- updateLocation 方法记录 "Location ID: null"
我确认未重新创建视图,并且在更新模型 JSF 阶段调用值为 null 的 setId(Long id) 方法。
为什么 JSF 更新模型阶段在第二次和添加回发期间将我的实体的 ID 设置为空?
问题的根本原因是在 JSF 生命周期的更新模型阶段,某些东西在我的实体上调用 setId(null) 方法。我在 setId() 方法中使用 StackTraceElement[] 数组来检查调用方法,并确定 UIViewParameter class 的 updateModel 方法是罪魁祸首。
经过一些谷歌搜索,我找到了提供 "Stateless mode to avoid unnecessary conversion, validation and model updating on postbacks" 的 OmniFaces viewParam Showcase。在更改我的代码以使用 OmniFaces viewParam 组件后,JSF 更新模型基础结构在回发期间停止调用我的实体上的 setId() 方法,一切都像在 GlassFish 4.1.2 中一样工作。
解决方案是从标准 f:viewParam 迁移到 OmniFaces o:viewParam。
我在从 GlassFish 4.1.2 升级到 GlassFish 5.1.0 的过程中遇到了一个问题,即 JSF 生命周期的更新模型阶段正在设置我的 @ 的 ID 属性 的值ViewScoped CDI 支持 beans 为 null。以下是我的简化视图。
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
template="/WEB-INF/templates/modena.xhtml">
<ui:define name="title">Location Editor2</ui:define>
<ui:define name="content">
<f:metadata>
<f:viewParam name="id" value="#{locationEditor2.location.id}" />
<f:viewAction action="#{locationEditor2.setLocationById}" />
</f:metadata>
<h:form id="location-editor-form">
<p:commandButton value="Update" styleClass="RaisedButton"
action="#{locationEditor2.updateLocation}" update="@form" />
</h:form>
</ui:define>
</ui:composition>
以下是我简化的 CDI backing bean。
import java.io.Serializable;
import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.elliottlogic.elis.ejb.location.Location;
import com.elliottlogic.elis.ejb.location.LocationManager;
import com.elliottlogic.elis.ejb.security.AuthorizationException;
import com.elliottlogic.elis.wa.core.ExceptionTools;
/**
* The <code>LocationEditor2</code> class TODO Complete Type Description
*
* @author jre
* @version $Id: $
*
*/
@Named
@ViewScoped
public class LocationEditor2 implements Serializable {
final static Logger logger = LoggerFactory.getLogger(LocationEditor2.class.getName());
static final long serialVersionUID = 1L;
private Location location = new Location();
@EJB
private LocationManager locationManager;
/**
* Configures model using persisted values.
*
* @throws AuthorizationException
*/
public void setLocationById() throws AuthorizationException {
try {
location = locationManager.readLocationByID(location.getId());
} catch (EJBException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Location not found.", "Location not found."));
ExceptionTools.unwrapEJBException(FacesContext.getCurrentInstance(), e);
}
}
/**
* Saves the changes to the location.
*
* @return
*/
public String updateLocation() {
logger.debug("Location ID: {}", location.getId());
return null;
}
/**
* @return the location
*/
public Location getLocation() {
return location;
}
/**
* @param location
* the location to set
*/
public void setLocation(Location location) {
this.location = location;
}
}
下面演示了这个问题:
- 将浏览器指向 LocationEditor2.xhtml?id=1
- Select [更新] 按钮。
- updateLocation 方法记录 "Location ID: 1"
- Select [更新] 按钮
- updateLocation 方法记录 "Location ID: null"
我确认未重新创建视图,并且在更新模型 JSF 阶段调用值为 null 的 setId(Long id) 方法。
为什么 JSF 更新模型阶段在第二次和添加回发期间将我的实体的 ID 设置为空?
问题的根本原因是在 JSF 生命周期的更新模型阶段,某些东西在我的实体上调用 setId(null) 方法。我在 setId() 方法中使用 StackTraceElement[] 数组来检查调用方法,并确定 UIViewParameter class 的 updateModel 方法是罪魁祸首。
经过一些谷歌搜索,我找到了提供 "Stateless mode to avoid unnecessary conversion, validation and model updating on postbacks" 的 OmniFaces viewParam Showcase。在更改我的代码以使用 OmniFaces viewParam 组件后,JSF 更新模型基础结构在回发期间停止调用我的实体上的 setId() 方法,一切都像在 GlassFish 4.1.2 中一样工作。
解决方案是从标准 f:viewParam 迁移到 OmniFaces o:viewParam。