Spring MVC 如何解析和验证处理程序方法参数?

How does Spring MVC resolve and validate handler method parameters?

我是 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 class:

的实例
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 验证注解声明在 username 密码 字段。

这里第一个疑问:与 2 使用的库 javax.validationorg.hibernate.validator[=60 到底有什么区别=] ?

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

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

@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 模型对象中定义的注释定义的错误消息。具体如何运作?

Tnx

what exactly is the difference from the 2 used library javax.validation and org.hibernate.validator?

Bean Validation 是一个 Java 规范,它允许您通过注释表达对对象模型的约束,允许您以可扩展的方式编写自定义约束。 这只是一个规范,根本没有实现。 Hibernate Validator 是这个规范的一个实现。 Hibernate Validator 完全实现了Bean Validation,它还具有一些hibernate 独有的特性。简而言之,javax.validation 是规范部分,org.hibernate.validator 是休眠独有的功能。

Can I do the same thing using only the hibernate validator library? (I think that I can specify the valid lenght of a string using the Hibernate validator, or not)?

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

Why in the tutorial use both?

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

It take this object as input parameter: @Valid User user. Who pass to it? I think that it could depend by the fact that in the form I specified commandName="user" so Spring automatically do it. Is it correct?

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

Why this @Valid annotation is related only to the javax.validation library and not also to the Hibernate validator (so this @Valid annotation validate also the field annotated with Hibernate validator annotation? why?)

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

And here the first doubt: what exactly is the difference between the 2 used libraries javax.validation and org.hibernate.validator?

javax.validation 来自 JSR-303 API。例如,您可以在 this Maven dependency.

中查看 API classes

Hibernate validator is one of the implementations of JSR 303 (reference implementation actually), so it implements all of the API, but adds its own extensions, for instance the @NotBlank annotation you mention. Other implementation of JSR 303 is for instance Apache BVal.


It takes this object as input parameter: @Valid User user. Who passes it to the handler method?

Spring MVC 中处理程序方法的值由接口 HandlerMethodArgumentResolver. In your case the implementation that will be called to resolve User parameter will probably be ServletModelAttributeMethodProcessor 的实现提供。您可以查看这些 classes 的源代码和 JavaDocs 以了解它们的内部工作方式。


The @Valid annotation will automatically call the validation process. How does this happend? Does it use AOP? Why this @Valid annotation is related only to the javax.validation library and not also to the Hibernate validator (so this @Valid annotation validate also the field annotated with Hibernate validator annotation?)

校验过程由ModelAttributeMethodProcessor调用,也就是前面提到的classServletModelAttributeMethodProcessor继承而来。它包含以下方法:

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 将调用验证。它可能是支持验证组的 JSR 303 的 @Valid 或 Spring 的 @Validated。它甚至可以是您的自定义注释,只要其名称以 Valid.

开头即可

From what I have understand if the user inserts wrong values into the login form I can obtain this error from the BindingResult handler parameter. Using the debugger I can see that this object contain the error message defined by the annotation defined into the User model object. How exactly works?

让我们回到ModelAttributeMethodProcessorclass。在其 resolveArgument 方法中有以下代码:

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

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


TLDR:你在这个问题中提出的很多问题都可以追溯到 HandlerMethodArgumentResolver 的各种实现。我建议通读这些 classes 并使用调试器逐步完成它们以更好地理解它们。

  1. 正确,称为属性绑定
  2. Spring 3+ 通过 @Valid 注释支持 JSR303Bean 验证,如果类路径上有任何 JSR 303 验证器框架的话。 Hibernate Validator 是 JSR 303 参考实现之一

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

    if(br.getAllErrors().size() > 0){ System.out.println("Server side validation takes place...."); }

总结,Spring MVC 将在 binding its properties 之后使用来自使用 Spring 的 JSP 形式的输入验证由 @Valid 注释注释的模型对象s 表单标签。任何违反约束的行为都将在 BindingResult 对象中显示为错误 here is a good post