【问题标题】:Is it normal behavior of JSF session-scoped validator?这是 JSF 会话范围验证器的正常行为吗?
【发布时间】:2015-11-03 16:20:03
【问题描述】:

实现:org.glassfish 2.2.12

我有以下会话范围的验证器:

@ManagedBean
@SessionScoped
public class CreateGroupNameValidator extends LengthValidator implements Serializable{ 

    @ManagedProperty(value="#{myDao}")
    private MyDao myDao;
    //Validate methods
}

尽管是会话范围和Serializable,但验证器在回发时无法恢复属性myDao 的值。我使用调试器并发现状态由 StateHolderSaver 类保存,该类具有以下构造函数:

public StateHolderSaver(FacesContext context, Object toSave) {
    className = toSave.getClass().getName();

    if (toSave instanceof StateHolder) {
        // do not save an attached object that is marked transient.
        if (!((StateHolder) toSave).isTransient()) {
            Serializable [] tuple = new Serializable[StateHolderTupleIndices.LastMember.ordinal()];

            tuple[StateHolderTupleIndices.StateHolderSaverInstance.ordinal()] =
                  (Serializable) ((StateHolder) toSave).saveState(context);
            if (toSave instanceof UIComponent) {
                tuple[StateHolderTupleIndices.ComponentAddedDynamically.ordinal()] = ((UIComponent)toSave).getAttributes().containsKey(DYNAMIC_COMPONENT) ? Boolean.TRUE : Boolean.FALSE;
            }
            savedState = tuple;
        } else {
            className = null;
        }
    } else if (toSave instanceof Serializable) {
        savedState = (Serializable) toSave;
        className = null;
    }
}

所以,由于 LenghtValidator 实现了 javax.faces.component.StateHolder 它并没有保存我的初始 Dao 值。这是正常行为吗?

【问题讨论】:

  • 什么是MyDao?该名称暗示它是一个服务类或 EJB,如果是这种情况,则不能使用 @ManagedProperty 注入(并且需要像 @EJB@Inject@Autowired 这样的注释)。真正的验证器(或转换器)通常只能具有两个范围中的任何一个,请求范围(@FacesValidator)或应用程序范围。其余的范围没有多大意义。
  • @Tiny 这是一颗春豆。

标签: jsf jsf-2.2


【解决方案1】:

这确实是指定的行为。另见 a.o. Validator javadoc:

...

Validator 实现必须有一个零参数的公共构造函数。此外,如果 Validator 类希望通过视图保存和恢复配置属性值,则实现还必须实现 StateHolder

...

转换器和验证器可以保存在 JSF 状态中,以便 JSF 实现可以确保它们在恢复视图后具有与渲染前一个请求的视图时完全相同的预期属性值(例如 minimum 和 @ 987654327@ 在LengthValidator 的情况下,可能会发生它们引用动态EL 表达式)。

虽然我必须承认他们在设计 JSF 1.0 规范(转换器/验证器仍然基于该规范)时确实没有真正考虑过在 JSF 转换器/验证器中注入业务服务实例的可能性。您当然不想将它保存在 JSF 视图状态中。如果是托管属性(因此不是 EJB/CDI 代理),它只会破坏 JSF 视图状态(因此在服务器端状态保存的情况下也会破坏 HTTP 会话)。

如果您不需要验证器具有 JSF 状态感知能力,请使用组合而不是继承。

public class CreateGroupNameValidator { 

    private LengthValidator lengthValidator;

    public CreateGroupNameValidator() {
        lengthValidator = new LengthValidator();
    }

    // ...
}

尽管如此,将验证器放在会话范围内有点可疑。这意味着验证器的行为特定于当前的 HTTP 会话。我想不出合理的现实世界用例,因为它们本质上是视图范围的(不是验证器实例,而是验证器属性)。通常会话范围的数据(例如登录用户)是在线程本地基础上注入/解析的。如果它是有状态的(即验证器属性可能因每个请求/视图而异),则最好将其设为请求范围,或者如果它是无状态的(即验证器属性在整个应用程序的生命周期内相同),则最好将其设为应用范围。

【讨论】:

  • 无法为此考虑合理的现实世界用例,因为它们本质上是视图范围的确实,ApplicationScoped 会更好。
  • 您当然不想将它保存在 JSF 视图状态。 实际上,从安全角度来看,它并不安全。但是如果对象没有实现StateHolder,而是Serializable,如果不在javax.faces.viewState中,什么时候保存呢? savedState = (Serializable) toSave; className = null; 很难说什么或者它是一个依赖于实现的事情,我们实际上并不关心它?
  • 如果它实现了Serializable,那么它也保存在状态中。 JSF 规范说附加到组件实例(转换器、验证器和侦听器)的对象也保存在状态中。
  • 但是在客户端视图状态的情况下,我们将我们的 EJB 暴露给客户端。安全吗?数据库相关的 EJB 通常包含 DataSource 实现,该实现又包含 DB-url 和用户/密码 Properties
  • EJB(和 CDI bean)被注入为proxies
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 2011-10-16
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 1970-01-01
  • 2013-03-28
相关资源
最近更新 更多