繁体   English   中英

@ViewScoped bean的意外生存

[英]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对象的stateMaprigheSelezionate属性将由基础引擎(后者通过(取消)选择侦听器)正确更新。 之后,我单击Modifica按钮(意大利语为Edit ),* cellEditor * s被正确激活。 到目前为止,一切都很好。

问题来了。 如果更改地址栏中的执行键,则获取类似http://<project base URL>/<flow path>?execution=<execution key 2> ,然后按Return键(因此启动该流的另一个实例) ,流程的开始视图再次被打开,但是“表的状态”看起来并未重置。 我选择的行仍然处于选中状态,并且BlocchiBean对象的stateMaprigheSelezionate属性看起来没有变化。 这非常奇怪,尤其是因为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM