Java 使用JSF#view.attributes映射而不是#viewScope来存储视图范围的应用程序数据是否不好?

Java 使用JSF#view.attributes映射而不是#viewScope来存储视图范围的应用程序数据是否不好?,java,jsf,myfaces,mojarra,view-scope,Java,Jsf,Myfaces,Mojarra,View Scope,有人建议我使用#view.attributes映射存储(客户端状态保存)。现在,它完美地满足了我的要求,但我只想确保这不是一个坏的做法或做坏事 我发现它的工作原理与viewscope映射完全相同,只是它即使在用户会话被破坏后也能保存数据。在2.0之前的JSF版本中,只有使用UIViewRoot.set/getAttribute才能在视图中存储对象 然而,在JSF2.0中引入了一个单独的视图范围,可以通过#{viewScope}在EL中使用,也可以通过UIViewRoow.getViewMap()

有人建议我使用
#view.attributes
映射存储(客户端状态保存)。现在,它完美地满足了我的要求,但我只想确保这不是一个坏的做法或做坏事


我发现它的工作原理与viewscope映射完全相同,只是它即使在用户会话被破坏后也能保存数据。

在2.0之前的JSF版本中,只有使用UIViewRoot.set/getAttribute才能在视图中存储对象

然而,在JSF2.0中引入了一个单独的视图范围,可以通过#{viewScope}在EL中使用,也可以通过UIViewRoow.getViewMap()编程使用。建议使用视图范围。它是使用保存在UIViewRoot中的映射实现的,并以与W视图状态中的视图属性相同的方式序列化,因此它与视图属性具有相同的生命周期

更新

根据MyFaces团队的莱昂纳多·乌里韦的说法:

在JSF2.2中,决定始终在会话中存储视图范围bean (请查看中@ViewScope注释的说明。) javadoc)。但是您只需调用facesContext.getViewRoot()并使用 属性映射。请记住,这些值必须是可序列化的或 实行国家所有制


因此,似乎可移植的方法是在UIViewRoot中使用属性映射。

建议使用
viewScope
。出于以下两个原因,避免使用视图根(通常为组件)属性:

  • 理论上,存在覆盖视图根组件属性的可能性。实际上这是不可能的,因为属性键是用
    枚举实现的(至少在mojarra中是这样)

  • UIComponentBase.getAttributes
    返回特定的
    Map
    实现:
    attributemap
    。这个实现首先检查组件中是否存在一个名为map key的方法。如果没有,它会检查内部映射。如果再次未找到,则检查组件
    ValueExpression
    map。所以它根本不是有效的,在非常特殊的情况下,可能导致无限递归

  • 查看
    attributemap.get
    ,例如:

    public Object get(Object keyObj) {
        String key = (String) keyObj;
        Object result = null;
        if (key == null) {
            throw new NullPointerException();
        }
        if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
            result = component.getStateHelper().get(UIComponent.PropertyKeysPrivate.attributesThatAreSet);
        }
        Map<String,Object> attributes = (Map<String,Object>)
              component.getStateHelper().get(PropertyKeys.attributes);
        if (null == result) {
            PropertyDescriptor pd =
                    getPropertyDescriptor(key);
            if (pd != null) {
                try {
                    Method readMethod = pd.getReadMethod();
                    if (readMethod != null) {
                        result = (readMethod.invoke(component,
                                EMPTY_OBJECT_ARRAY));
                    } else {
                        throw new IllegalArgumentException(key);
                    }
                } catch (IllegalAccessException e) {
                    throw new FacesException(e);
                } catch (InvocationTargetException e) {
                    throw new FacesException(e.getTargetException());
                }
            } else if (attributes != null) {
                if (attributes.containsKey(key)) {
                    result = attributes.get(key);
                }
            }
        }
        if (null == result) {
            ValueExpression ve = component.getValueExpression(key);
            if (ve != null) {
                try {
                    result = ve.getValue(component.getFacesContext().getELContext());
                } catch (ELException e) {
                    throw new FacesException(e);
                }
            }
        }
    
        return result;
    }
    
    公共对象获取(对象keyObj){
    字符串键=(字符串)keyObj;
    对象结果=空;
    if(key==null){
    抛出新的NullPointerException();
    }
    如果(属性为设置键等于(键)){
    结果=component.getstateheloper().get(UIComponent.PropertyKeysPrivate.attributesThatAreSet);
    }
    映射属性=(映射)
    component.getStateHelper().get(PropertyKeys.attributes);
    if(null==结果){
    PropertyDescriptor pd=
    getPropertyDescriptor(键);
    如果(pd!=null){
    试一试{
    方法readMethod=pd.getReadMethod();
    if(readMethod!=null){
    结果=(readMethod.invoke(组件,
    空的(对象(数组));
    }否则{
    抛出新的IllegalArgumentException(键);
    }
    }捕获(非法访问例外e){
    抛开新面孔(e);
    }捕获(调用TargetException e){
    抛出新的FacesException(例如getTargetException());
    }
    }else if(属性!=null){
    if(attributes.containsKey(键)){
    结果=attributes.get(键);
    }
    }
    }
    if(null==结果){
    ValueExpression ve=组件。getValueExpression(键);
    如果(ve!=null){
    试一试{
    结果=ve.getValue(component.getFacesContext().getELContext());
    }捕捉(电子例外e){
    抛开新面孔(e);
    }
    }
    }
    返回结果;
    }
    

    至少从网络应用的角度来看,考虑到你的需求,这没有什么意义。视图状态会记住组件状态:从初始状态更改的组件模型值,将在下一步进行处理。如果这些值不需要处理,那么就不需要长时间的记忆。我们可以把它们看作是“暂时的”。相反,如果它们必须被处理和记住很长一段时间,那么持久性就是方法。事实上,我认为这种数据的生存时间比会话时间长,比永久时间短(持久性)的情况没有一个

    你能告诉我们一个真实的例子吗

    我脑海中出现的最好的例子是记住tabView或手风琴的活动索引,但是如果这个值很重要的话,它可以(而且应该)保持


    然而,每个问题都有一个解决方案,我能想到的第一件事是,您可以实现一个自定义作用域,使用特定的cookie(客户端)值作为键将这些值存储在应用程序作用域中。

    但是viewscope存储在会话中&因此,当用户会话被破坏时,它会丢失(在我的应用程序中发生得非常快),那么,如何使viewscope数据即使在会话被破坏后仍然存在?我应该为此数据设计自定义实现吗?有什么想法吗?如果您的状态保存方法是服务器,则该视图将在会话中序列化。如果方法是client,则视图状态将被序列化、编码为base64并发送到浏览器,在浏览器中它存储在隐藏字段中,并在执行下一个请求时发送回服务器。因此,即使会话在服务器上过期,客户端也将提供该视图状态,并将成功恢复。查看UIViewRoot的实现,组件属性与视图映射一起保存,因此它们的生命周期应该相同。您使用的是什么JSF实现和版本?感谢Adrian对此进行深入研究。我使用的是
    Myfaces 2.2.2
    。但我确实试过了