[英]Unexpected survival of a @ViewScoped bean
我正在開發包含物件創作形式和現有的對象列表,creablocco.xhtml的觀點 :
<?xml version="1.0"?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:ace="http://www.icefaces.org/icefaces/components"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
template="/resources/includes/templates/template.xhtml">
<ui:param name="pageTitle" value="Crea blocco" />
<ui:define name="page-content">
<ace:panel id="personalInfoInputPanel" header="Crea blocco">
<div class="div_creablocco">
<h:form id="creabloccoform">
<h:panelGrid id="creabloccoInputGrid" columns="1" styleClass="table_creablocco">
<ace:textEntry id="numeroFile" value="#{creaBloccoBean.numeroFile}" label="Numero file:" labelPosition="left" required="true" requiredIndicator="(*)" indicatorPosition="right">
</ace:textEntry>
<ace:textEntry id="numeroColonne" value="#{creaBloccoBean.numeroColonne}" label="Numero colonne:" labelPosition="left" required="true" requiredIndicator="(*)" indicatorPosition="right">
</ace:textEntry>
<ace:textEntry id="posizioneX" value="#{creaBloccoBean.posizioneX}" label="Coordinata X:" labelPosition="left" required="true" requiredIndicator="(*)" indicatorPosition="right">
</ace:textEntry>
<ace:textEntry id="posizioneY" value="#{creaBloccoBean.posizioneY}" label="Coordinata Y:" labelPosition="left" required="true" requiredIndicator="(*)" indicatorPosition="right">
</ace:textEntry>
<ace:pushButton id="pushBttnCreaBlocco" value="Crea blocco" action="creaBlocco"/>
</h:panelGrid>
</h:form>
</div>
</ace:panel>
<ace:panel id="blocchiPanel" header="Blocchi">
<h:form id="blocchiTableForm">
<ace:menuBar id="iconeBloccoMenu" rendered="#{!blocchiBean.modificaInCorso}">
<ace:menuItem id="dettagliBloccoButton" value="Dettagli" action="dettagliBlocco" icon="ui-icon ui-icon-search" />
<ace:menuItem id="modificaBloccoButton" value="Modifica" action="modificaBlocco" icon="ui-icon ui-icon-pencil" />
<ace:menuItem id="cancellaBloccoButton" value="Cancella" action="cancellaBlocco" icon="ui-icon ui-icon-closethick" />
</ace:menuBar>
<ace:menuBar id="iconeModificaBloccoMenu" rendered="#{blocchiBean.modificaInCorso}">
<ace:menuItem id="annullaModificheButton" value="Annulla" action="annullaModificheBlocco" icon="ui-icon ui-icon-arrow-1-w" />
<ace:menuItem id="salvaModifiche" value="Salva" action="salvaModificheBlocco" icon="ui-icon ui-icon-disk" />
</ace:menuBar>
<ace:dataTable id="blocchiTable" value="#{blocchi}"
binding="#{blocchiBean.table}" stateMap="#{blocchiBean.stateMap}"
selectionMode="multiple" doubleClickSelect="false"
rowSelectListener="#{blocchiBean.selectListener}"
rowUnselectListener="#{blocchiBean.deselectListener}" var="blocco"
lazy="true" rows="10" paginator="true" paginatorPosition="bottom">
<ace:column id="idBloccoColumn" headerText="ID" styleClass="id">
<h:outputText id="idBloccoText" value="#{blocco.id}" />
</ace:column>
<ace:column id="numeroFileBloccoColumn" headerText="Numero file"
styleClass="numFile">
<ace:cellEditor>
<f:facet name="output">
<h:outputText id="numeroFileBloccoText"
value="#{blocco.numeroFile}" />
</f:facet>
<f:facet name="input">
<h:inputText id="numeroFileBloccoInput"
value="#{blocco.numeroFile}" />
</f:facet>
</ace:cellEditor>
</ace:column>
<ace:column id="numeroColonneBloccoColumn"
headerText="Numero colonne" styleClass="numColonne">
<ace:cellEditor>
<f:facet name="output">
<h:outputText id="numeroColonneBloccoText"
value="#{blocco.numeroColonne}" />
</f:facet>
<f:facet name="input">
<h:inputText id="numeroColonneBloccoInput"
value="#{blocco.numeroColonne}" />
</f:facet>
</ace:cellEditor>
</ace:column>
<ace:column id="posizioneXBloccoColumn" headerText="Posizione X"
styleClass="posX">
<ace:cellEditor>
<f:facet name="output">
<h:outputText id="posizioneXBloccoText"
value="#{blocco.posizioneX}" />
</f:facet>
<f:facet name="input">
<h:inputText id="posizioneXBloccoInput"
value="#{blocco.posizioneX}" />
</f:facet>
</ace:cellEditor>
</ace:column>
<ace:column id="posizioneYBloccoColumn" headerText="Posizione Y"
styleClass="posY">
<ace:cellEditor>
<f:facet name="output">
<h:outputText id="posizioneYBloccoText"
value="#{blocco.posizioneY}" />
</f:facet>
<f:facet name="input">
<h:inputText id="posizioneYBloccoInput"
value="#{blocco.posizioneY}" />
</f:facet>
</ace:cellEditor>
</ace:column>
</ace:dataTable>
</h:form>
</ace:panel>
</ui:define>
</ui:composition>
“對象創建表單”( <h:form id="creabloccoform">
)由文本輸入和按鈕( <ace:pushButton id="pushBttnCreaBlocco" ... />
)組成。
“現有對象列表”是一個表( <ace:dataTable id="blocchiTable" .. />
),它是編輯所選對象屬性或刪除所選對象的起點。
通過選擇表中的某些行並按菜單欄中的相應按鈕,可以完成刪除和編輯過程(請查看<ace:menuBar id="iconeBloccoMenu" .. />
和<ace:menuBar id="iconeModificaBloccoMenu" ... />
)。
這是使用Spring Webflow creablocco-flow.xml定義的流程 :
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<var name="creaBloccoBean" class="com.infoone.siglo.viewbeans.CreaBloccoBean" />
<view-state id="creablocco">
<on-entry>
<evaluate expression="blocchiBean.getBlocchi()" result="flowScope.blocchi" />
</on-entry>
<transition on="creaBlocco" to="fine">
<evaluate expression="gestoreBlocchiCtr.creaBlocco(creaBloccoBean)" />
</transition>
<transition on="cancellaBlocco" to="decidiCancellazione">
<evaluate expression="blocchiBean.getRigheSelezionate()" result="flowScope.righeSelezionate"/>
</transition>
<transition on="modificaBlocco" to="decidiModifica">
<evaluate expression="blocchiBean.getRigheSelezionate()" result="flowScope.righeSelezionate"/>
</transition>
</view-state>
<decision-state id="decidiCancellazione">
<if test="!(flowScope.righeSelezionate).isEmpty()" then="cancellaBlocco" else="fine" />
</decision-state>
<decision-state id="decidiModifica">
<if test="!(flowScope.righeSelezionate).isEmpty()" then="modificaBlocco" else="fine" />
</decision-state>
<action-state id="cancellaBlocco">
<evaluate expression="gestoreBlocchiCtr.rimuoviBlocchi(flowScope.righeSelezionate)" />
<transition to="fine"/>
</action-state>
<action-state id="modificaBlocco">
<evaluate expression="blocchiBean.abilitaModifica()"/>
<transition to="fine"/>
</action-state>
<end-state id="fine" />
</flow>
這是導致標題BlocchiBean.java中提到的問題的bean :
package com.infoone.siglo.viewbeans;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import javax.annotation.PostConstruct;
import javax.faces.bean.ViewScoped;
import org.icefaces.ace.component.column.Column;
import org.icefaces.ace.component.datatable.DataTable;
import org.icefaces.ace.event.SelectEvent;
import org.icefaces.ace.event.UnselectEvent;
import org.icefaces.ace.model.table.RowState;
import org.icefaces.ace.model.table.RowStateMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.infoone.siglo.viewbeans.lazyLoaders.BlocchiLazyLoader;
@Component
@ViewScoped
public class BlocchiBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = -7007046069743684498L;
@Autowired
private transient BlocchiLazyLoader blocchi;
private DataTable table;
private RowStateMap stateMap;
private Set<CreaBloccoBean> righeSelezionate;
private boolean modificaInCorso;
public BlocchiBean()
{
}
@SuppressWarnings("unused")
@PostConstruct
private void initBlocchi()
{
stateMap = new RowStateMap();
stateMap.setAllSelectable(true);
righeSelezionate = new ConcurrentSkipListSet<CreaBloccoBean>();
blocchi.setRowCount(blocchi.getPageSize());
modificaInCorso = false;
}
//BlocchiLazyLoader blocchi
public void setBlocchi(BlocchiLazyLoader blocchi) {
this.blocchi = blocchi;
}
public BlocchiLazyLoader getBlocchi() {
return blocchi;
}
//RowStateMap stateMap
public RowStateMap getStateMap() {
return stateMap;
}
public void setStateMap(RowStateMap stateMap) {
this.stateMap = stateMap;
}
//DataTable table
public DataTable getTable() {
return table;
}
public void setTable(DataTable table) {
this.table = table;
}
//Selection listeners
public void selectListener(SelectEvent event) {
Object[] oggettiSelezionati = event.getObjects();
for (Object oggettoSelezionato : oggettiSelezionati) {
CreaBloccoBean bloccoSelezionato = (CreaBloccoBean) oggettoSelezionato;
righeSelezionate.add(bloccoSelezionato);
}
}
public void deselectListener(UnselectEvent event) {
Object oggettoDeselezionato = event.getObject();
CreaBloccoBean bloccoDeselezionato = (CreaBloccoBean) oggettoDeselezionato;
righeSelezionate.remove(bloccoDeselezionato);
}
//List<CreaBloccoBean> righeSelezionate
public List<CreaBloccoBean> getRigheSelezionate() {
CreaBloccoBean[] array = new CreaBloccoBean[righeSelezionate.size()];
array = righeSelezionate.toArray(array);
return Arrays.asList(array);
}
public void setRigheSelezionate(List<CreaBloccoBean> righeSelezionate) {
this.righeSelezionate.clear();
this.righeSelezionate.addAll(righeSelezionate);
}
public boolean isModificaInCorso() {
return modificaInCorso;
}
public void setModificaInCorso(boolean modificaInCorso) {
this.modificaInCorso = modificaInCorso;
}
//Abilitazione dell'editing
public void abilitaModifica() {
if (!righeSelezionate.isEmpty())
{
modificaInCorso = true;
List<Column> colonne = table.getColumns();
for (Object oggettoCorrente : stateMap.getSelected()) {
CreaBloccoBean beanCorrente = (CreaBloccoBean) oggettoCorrente;
RowState statoCorrente = stateMap.get(beanCorrente);
if (righeSelezionate.contains(beanCorrente)) {
for (Column colonna : colonne) {
statoCorrente.addActiveCellEditor(colonna.getCellEditor());
}
}
}
}
}
}
現在,假設我正在使用以下URL訪問流: http://<project base URL>/<flow path>?execution=<execution key 1>
然后選擇其中一行。 顯然, BlocchiBean對象的stateMap和righeSelezionate屬性將由基礎引擎(后者通過(取消)選擇偵聽器)正確更新。 之后,我單擊Modifica按鈕(意大利語為Edit ),* cellEditor * s被正確激活。 到目前為止,一切都很好。
問題來了。 如果更改地址欄中的執行鍵,則獲取類似http://<project base URL>/<flow path>?execution=<execution key 2>
,然后按Return鍵(因此啟動該流的另一個實例) ,流程的開始視圖再次被打開,但是“表的狀態”看起來並未重置。 我選擇的行仍然處於選中狀態,並且BlocchiBean對象的stateMap和righeSelezionate屬性看起來沒有變化。 這非常奇怪,尤其是因為BlocchiBean被聲明為@ViewScoped :
@Component
@ViewScoped
public class BlocchiBean implements Serializable {
因此,期望在新的流實例啟動時從頭開始重新創建它。
我已經嘗試過使用示波器,但是並不能解決問題。
為了完整起見 ,我提到了BlocchiBean對象的先前注釋:
@Component("blocchiBean")
@ViewScoped
public class BlocchiBean implements Serializable {
我的目標是使組件名稱與視圖和流程中使用的bean名稱匹配。 不幸的是,這個注釋也不能解決問題。
我找到了補救辦法。
我尚無法完全解釋為什么我的問題中提供的解決方案無法正常工作,但至少我提出了正確的解決方案。
我在流程中聲明了流程變量:
<var name="blocchiBean" class="com.infoone.siglo.viewbeans.BlocchiBean" />
並從類定義中刪除了所有注釋:
package com.infoone.siglo.viewbeans;
//imports
public class BlocchiBean implements Serializable {
對於那些做到這一點的人, 請不要在xml配置文件中將其顯式聲明為bean 。 它只能是一個Spring Flow變量,別無其他。
現在可以了。 無論如何,不要期望Spring Web Flow在相同的流實例中始終使用相同的BlocchiBean對象,即使它本質上是flowScoped(它是一個流變量,因此它是flowScoped)。
確實,如果您運行調試器並查看對象ID,則會發現Spring Web Flow在同一流實例中使用不同的實例。 乍一聽聽起來很奇怪,但最后卻奏效了:確實,每當一個bean實例被垃圾回收時,它的狀態就被適當地保存在“某處”,這樣就可以通過其流設置同一流執行中下一個實例的狀態。 setter方法,僅在實例化之后。
顯然,如果流的另一個實例開始,將構造一個新的新流變量,並且不會通過調用其setter方法來更改其狀態。 換句話說,它將作為flowScoped變量預期的行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.