实施:org.glassfish 2.2.12
我有以下会话范围的验证器:
@ManagedBean
@SessionScoped
public class CreateGroupNameValidator extends LengthValidator implements Serializable{
@ManagedProperty(value="#{myDao}")
private MyDao myDao;
//Validate methods
}
尽管是会话范围和Serializable
,但验证者在回发到来时无法恢复属性myDao
的值。我使用了调试器和figdOut状态由具有以下构造函数的类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值。这是正常行为吗?
答案 0 :(得分:3)
这确实是指定的行为。另见a.o. Validator
javadoc:
...
Validator
实现必须具有零参数的公共构造函数。此外,如果Validator类希望通过视图保存和恢复配置属性值,则实现还必须实现StateHolder
。...
转换器和验证器可以保存在JSF状态,以便JSF实现可以确保它们在恢复视图之后具有与在呈现先前请求的视图期间完全相关的属性值(例如minimum
在maximum
的情况下LengthValidator
,可能会发生它们引用动态EL表达式)。
虽然我必须承认他们在设计JSF 1.0规范(转换器/验证器仍然基于)时没有考虑过在JSF转换器/验证器中注入业务服务实例的可能性。您当然不希望将其保存在JSF视图状态中。对于托管属性(因此不是EJB / CDI代理),它只会炸毁JSF视图状态(因此在服务器端状态保存的情况下也会破坏HTTP会话)。
如果您不需要验证JSF状态的验证器,请使用合成而不是继承。
public class CreateGroupNameValidator {
private LengthValidator lengthValidator;
public CreateGroupNameValidator() {
lengthValidator = new LengthValidator();
}
// ...
}
尽管如此,在会话范围内放置验证器有点可疑。这意味着验证器的行为特定于当前的HTTP会话。我无法想到合理的现实世界用例,因为它们本身就是视图范围(不是验证器实例,而是验证器属性)。通常,会话范围数据(例如登录用户)将在本地线程上注入/解析。如果它是有状态的(即验证器属性可能在每个请求/视图的基础上变化),或者如果它是无状态的应用程序作用域(即验证器属性在整个应用程序的生命周期内是相同的),你最好使它成为请求范围。