3

我是 Spring MVC 的新手,我已经导入了一个与服务器端验证相关的教程项目,我对它的工作原理有些怀疑。

所以我有这个名为login.jsp的登录页面,其中包含这个登录表单:

<form:form action="${pageContext.request.contextPath}/login" commandName="user" method="post">

    <table>

        <tr>
            <td><label>Enter Username : </label></td>
            <td><form:input type="text" path="username" name="username" />
                <br> <form:errors path="username" style="color:red;"></form:errors>
            </td>
        </tr>

        <tr>
            <td><label>Enter Password : </label></td>
            <td><form:input type="password" path="password" name="password" />
                <br> <form:errors path="password" style="color:red;"></form:errors>
            </td>
        </tr>

        <tr>
            <td>&nbsp</td>
            <td align="center"><input type="submit" value="Login" /></td>
        </tr>

    </table>

</form:form>

我认为使用从模型中检索到的commandName="user"属性指定的对象(如果我做错了断言,请纠正我)来存储用户插入的用户名和密码。

这个commandName="user"是这个User类的一个实例:

import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;

public class User {

    @NotBlank(message="Username can not be blank")
    private String username;

    @Size(min=6,message="Password must be atleast 6 characters long")
    private String password;

    private String gender;
    private String vehicle;
    private String country;
    private String image;

    ...............................................
    ...............................................
    GETTER AND SETTER METHODS
    ...............................................
    ...............................................
}

如您所见,在用户名密码字段上声明了@NotBlank@Size验证注释。

这里是第一个疑问:与 2 used library javax.validationorg.hibernate.validator到底有什么区别?

为什么在教程中同时使用两者?我可以只使用休眠验证器库来做同样的事情吗?(我认为我可以使用 Hibernate 验证器指定字符串的有效长度,或者不)?

因此,当登录表单被提交时,它会生成并向/login资源发出 HttpRequest,该资源由该方法处理并声明为控制器类:

@RequestMapping(value="/login" , method=RequestMethod.POST)
public String do_login(HttpServletRequest req , Model md , HttpSession session , @Valid User user, BindingResult br)
{
    try
    {
        //System.out.println(br.getAllErrors().size());

        String username = req.getParameter("username");
        String password = req.getParameter("password");

        System.out.println("Username and pasword are : "+username +"  "+ password);
        if(br.getAllErrors().size() > 0){
            System.out.println("Server side validation takes place....");
        }
        else{
        Login_Model lm = new Login_Model();
        String message = lm.do_login_process(username, password);

        if(message.equals("login success"))
        {
            session.setAttribute("username", username);
            return "redirect:/myprofile";
        }
        else
        {
            md.addAttribute("error_msg", message);
        }
        }
        return "login";
    }
    catch(Exception e)
    {
        return "login";
    }
}

好的,现在我对这种方法有以下疑问:

1) 将此对象作为输入参数:@Valid User user。谁传给它?我认为这可能取决于我在表单中指定commandName="user"的事实,因此 Spring 会自动执行此操作。这是对的吗?

2)据我所知,@Valid注释会自动调用验证过程。它是怎么发生的?与Spring提供的AOP功能有关吗?或者是什么?为什么这个@Valid注释只与javax.validation库有关,而不与 Hibernate 验证器相关(所以这个@Valid注释也验证用 Hibernate 验证器注释注释的字段?为什么?)

3)据我所知,如果用户在登录表单中插入错误的值,我可以通过BindingResult br输入参数获取此错误。使用调试器,我可以看到该对象包含由定义到User模型对象中的注释定义的错误消息。具体是如何工作的?

肿瘤坏死因子

4

3 回答 3

5

javax.validation这里是第一个疑问: 2 used library和之间到底有什么区别org.hibernate.validator

javax.validation来自 JSR-303 API。例如,您可以在此 Maven 依赖项中查看 API 类。

Hibernate 验证器是 JSR 303 的实现之一(实际上是参考实现),因此它实现了所有 API,但添加了自己的扩展,例如@NotBlank您提到的注释。JSR 303 的其他实现例如是Apache BVal


它将这个对象作为输入参数:@Valid User user. 谁将它传递给处理程序方法?

Spring MVC 中处理程序方法的值由 interface 的实现提供HandlerMethodArgumentResolver。在您的情况下,将被调用来解析User参数的实现可能是ServletModelAttributeMethodProcessor. 您可以查看这些类的源代码和 JavaDocs 以了解它们在内部是如何工作的。


注释将@Valid自动调用验证过程。这是怎么发生的?它使用AOP吗?为什么这个@Valid注解只与javax.validation库相关而不与 Hibernate 验证器相关(所以这个@Valid注解也验证了用 Hibernate 验证器注解注解的字段?)

验证过程由 调用ModelAttributeMethodProcessor,这是前面提到的类ServletModelAttributeMethodProcessor继承自。它包含以下方法:

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
    Annotation[] annotations = parameter.getParameterAnnotations();
    for (Annotation ann : annotations) {
        if (ann.annotationType().getSimpleName().startsWith("Valid")) {
            Object hints = AnnotationUtils.getValue(ann);
            binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
            break;
        }
    }
}

如果仔细观察,您将看到以下表达式的条件:

ann.annotationType().getSimpleName().startsWith("Valid")

这意味着如果参数有任何以 .开头的注释Valid,Spring 将调用验证。它可能是支持验证组@Valid的 JSR 303 或 Spring 。@Validated它甚至可以是您的自定义注解,只要它的名称以Valid.


据我了解,如果用户在登录表单中插入错误的值,我可以从BindingResult处理程序参数中获取此错误。使用调试器,我可以看到该对象包含由定义到 User 模型对象中的注释定义的错误消息。具体是如何工作的?

让我们回到ModelAttributeMethodProcessor课堂。resolveArgument其方法中有如下代码:

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);

WebDataBinder这将创建在前面提到的validateIfApplicable方法中调用验证的实例。验证本身填充BindingResult然后通过ErrorsMethodArgumentResolver类提供给控制器处理程序方法,该类再次实现HandlerMethodArgumentResolver.


TLDR:您在这个问题中提出的很多问题都可以追溯到HandlerMethodArgumentResolver. 我建议浏览这些类并使用调试器逐步完成它们以更好地理解它们。

于 2015-10-10T12:20:46.997 回答
2
  1. 正确,称为属性绑定
  2. Spring 3+ 支持JSR303通过注释进行 Bean 验证@Valid,如果类路径上有任何 JSR 303 验证器框架。Hibernate Validator 是 JSR 303 参考实现之一

  3. 任何约束违规都将作为 BindingResult 对象中的错误公开,因此您必须像您所拥有的那样检查控制器方法中的违规

    if(br.getAllErrors().size() > 0){ System.out.println("发生服务器端验证......"); }

总结,Spring MVC 将在使用来自使用 Spring 的表单标签的 JSP 表单的输入@Valid之后验证由注释注释的模型对象。binding its properties任何违反约束的行为都将被暴露为BindingResult对象 中的错误,这是一个很好的帖子

于 2015-10-10T12:27:05.750 回答
2

与使用的 2 个库 javax.validation 和 org.hibernate.validator 到底有什么区别?

Bean Validation 是一种 Java规范,它允许您通过注解表达对对象模型的约束,允许您以可扩展的方式编写自定义约束。它只是一个没有实现的规范。Hibernate Validator 是这个规范的一个实现。Hibernate Validator 完全实现了 Bean Validation,它也有一些 hibernate 独有的特性。简单来说,javax.validation就是spec部分,org.hibernate.validator是hibernate独有的特性。

我可以只使用休眠验证器库来做同样的事情吗?(我认为我可以使用 Hibernate 验证器指定字符串的有效长度,或者不)?

是的,你可以,但最好坚持使用javax.validation命名空间,只使用供应商特定命名空间的独特功能,例如org.hibernate.validator. 这种方法的好处是您可以将 Bean Validation 实现从 Hibernate Validator 切换到其他东西,而只需最少的更改

为什么在教程中同时使用两者?

因为@NotBlank约束在 Bean Validation 规范中不可用,仅由 Hibernate Validator 提供。

它将这个对象作为输入参数:@Valid User user。谁传给它?我认为这可能取决于我在表单中指定 commandName="user" 的事实,因此 Spring 会自动执行此操作。这是对的吗?

可能您有GET用于loginurl 的控制器,它返回一个空User对象,该对象又与 spring 标签绑定,例如<form:input type="text" path="username" name="username" />. 当用户填写表单并提交时,spring 会收集这些标签值并创建一个实例User并将其传递给控制器​​。

为什么这个 @Valid 注释只与 javax.validation 库有关,而不与 Hibernate 验证器相关(所以这个 @Valid 注释也验证用 Hibernate 验证器注释注释的字段?为什么?)

已经解释过,Spec/Impl 的故事!

于 2015-10-10T12:15:30.630 回答