如何传递属性至验证器并在PasswordValidatorImpl中获取完整selectedUser对象?
问题1:如何将属性传递至验证器并获取该属性?
你已经通过<f:attribute>标签实现了属性传递,在验证器中通过component.getAttributes().get("属性名")获取,但这种方式存在局限性:<f:attribute>的值是在**组件树构建阶段(页面加载时)**解析的,若属性值是动态变化的(比如编辑现有用户时),无法获取到最新的实例。
更可靠的两种实现方式:
方式1:通过EL表达式直接获取
在验证器中直接解析EL表达式获取目标对象,无需额外标签配置:@Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { // 直接解析EL表达式获取userView中的selectedUser ELContext elContext = context.getELContext(); User user = (User) elContext.getELResolver().getValue(elContext, null, "userView.selectedUser"); }方式2:Spring依赖注入
你的UserView是Spring管理的@ViewScoped组件,可直接将其注入到验证器中(需确保验证器也是Spring托管的bean):@Component public class PasswordValidatorImpl implements PasswordValidator, Validator<Object>, Serializable { @Autowired private UserView userView; @Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { User user = userView.getSelectedUser(); // 后续验证逻辑 } }
问题2:如何在PasswordValidatorImpl中获取用户在GUI中填写的完整userView.selectedUser对象?
注意JSF的生命周期顺序:验证阶段(Process Validations)早于更新模型值阶段(Update Model Values)。也就是说,在验证器执行时,用户填写的其他表单字段(如用户名、邮箱等)还未更新到selectedUser模型对象中,此时直接获取selectedUser只能拿到初始值或未更新的旧值。
根据需求有两种解决方案:
延后验证时机到模型更新后
将验证逻辑移到更新模型值阶段之后,通过<f:event type="postValidate">绑定到视图或表单上,此时模型对象已被表单值更新:<h:form> <!-- 你的密码组件及其他表单组件 --> <f:event type="postValidate" listener="#{passwordValidator.validateAfterModelUpdate}" /> </h:form>验证器中添加对应方法:
public void validateAfterModelUpdate(FacesEvent event) throws ValidatorException { User user = userView.getSelectedUser(); // 此时user的所有属性已被表单提交的值更新 if (!isValidPassword(user.getPassword())) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "密码无效", null)); FacesContext.getCurrentInstance().renderResponse(); // 终止后续流程 } }手动收集组件本地值(验证阶段获取)
如果必须在验证阶段获取完整提交值,可遍历表单组件,收集用户提交的原始值并手动组装对象:@Override public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { // 获取父表单组件 UIForm form = findParentForm(component); User user = new User(); // 遍历表单内组件,收集提交值 for (UIComponent child : form.getChildren()) { if (child instanceof EditableValueHolder) { EditableValueHolder evh = (EditableValueHolder) child; Object submittedValue = evh.getSubmittedValue(); // 根据组件ID映射到user属性 if ("username".equals(child.getId())) { user.setUsername((String) submittedValue); } else if ("email".equals(child.getId())) { user.setEmail((String) submittedValue); } else if ("pwd1".equals(child.getId())) { user.setPassword((String) submittedValue); } } } // 此时user包含所有表单提交的值,执行验证逻辑 } // 辅助方法:查找父表单组件 private UIForm findParentForm(UIComponent component) { if (component == null) return null; if (component instanceof UIForm) return (UIForm) component; return findParentForm(component.getParent()); }
补充说明
如果你仅需要selectedUser的对象引用(不需要其他属性的提交值),直接使用依赖注入或EL表达式获取的方式即可,这两种方式能拿到userView中管理的selectedUser实例,后续模型更新时该实例的属性会被自动填充。
内容的提问来源于stack exchange,提问作者vit_sin

