• SpringMVC关键问题讲解


    接着上篇文章,大家可能关心的那两个问题

    1.controller是怎样进行数据封装的

    要说这个问题,我不得不说SimpleFormController了

    SimpleFormController是AbstractFormController的具体实现,允许你在配置文件里通过successView和formView属性来配置成功视图(表单成功提交后要转向的页面)和表单视图(显示表单的页面);如果提交不合法(有三种可能:1.validator出错。2.bind错误,也就是说从请求中提取参数封装到command的过程中出现了类型转化错误,比如将一个含字母字符串转换为Integer。3.onBindAndValidate()方法出错),则会重新返回到表单视图;如果提交合法,onSubmit()方法的默认实现会转向成功页面,当然你可以覆写该方法在转向之前填充一些你想返回的信息。

        SimpleFormController的工作流与AbstractFormController差不多,唯一的不同是你不必自己去实现showForm()和processFormSubmission()。showForm()这个方法已经被类SimpleFormController实现了并被限定为final,你不可以在继承SimpleFormController的子类里覆写这个类。processFormSubmission()这个方法尽管可以去覆写但由于它几乎可以满足所有的要求,因此一般也不会有人去重写它。
    它的处理流程是这样的:   
    get请求来到时,这样处理:   
    1) 请求传递给一个controller对象   
    2) 调用formBackingObject()方法,创建一个command对象的实例。  
    3) 调用initBinder(),注册需要的类型转换器   
    4) 调用showForm()方法,返回准备呈现给用户的视图 ,如果“bindOnNewForm”属性设为true,则ServletRequestDataBinder会将初始请求参数填入一个新的表单对象,并且执行onBindOnNewForm()方法。
    5) 调用referenceData()方法,准备给用户显示相关的数据。如用户登录需要选择的年度信息  
    6) 返回formView指定的视图

    post请求来到时,这样处理:   
    1) 如果sessionForm属性没有设定,则调用formBackingObject()方法,创建一个command对象的实例。否则从session中取得表单对象  
    2) 将请求传来的参数写入command对象,看它的源代码,会发现它是这样来做的:

    ServletRequestDataBinder binder = createBinder(request, command);
    binder.bind(request);

    3)执行onBind()方法,在绑定数据之后,验证数据之前对表单数据进行一些自制的修改动作。   
    4) 如果设置为要求验证(validateOnBinding属性被设定),则调用validator类进行数据验证  
    5) 调用onBindAndValidate()方法,该方法允许自定义数据绑定和校验处理  
    6)执行processFormSubmission()检验 Errors对象中含不含错误,如果含有错误则执行showForm()返回到填写表单页面;否则执行onSubmit()方法,进行提交表单,然后转向成功页面。

    2.<spring:kind>的用法

    在Spring框架体系下,可以说规约最少,最不受限制的就是表现层技术了。不像Struts,改定了好多的标签,而且有些功能还和标签绑定了。Sping也定义了一些标签,但这些标签只是给使用者提供了一些方便,并不会提供额外的功能或效果。

    Sping Tag比较少,不超过十个,这里只介绍最常用的一个<sping:bind>,也叫作Spring绑定

    <sping:bind>的path属性制定了与表单中的那个属性绑定,这样,${status.expression}就代表了那个属性的名称,${status.expression}代表那个属性的值。如果path属性为“XXX.*”则与那个表单的所有属性绑定。

    上面的例子表单有两个属性username和password。实际上绑定方式有两种,第一种就像我的上一篇http://javacrazyer.iteye.com/blog/790834文章讲的的那样,第二种如下:

    ......
      <spring:bind path="loginForm">
          用户名:<INPUT name="userName" type="text" value="${command.userName}"/><br>
          密码:<INPUT name="password" type="password" value="${command.password}"/>
         </spring:bind>

    这样尽管也行,但在错误信息处理上,针对用户名和密码的报错信息不会像原来那样显示出来了


    所以验证我下面要说的话:

    使用<sping:bind>标签,在初次进入表单页面时并不会有什么作用,而是当表单提交后,如果有BindException错误时再返回这个页面时,可以把先前的输入显示在input里。

    说到错误信息处理,我又要许多要说了,来看看我前一篇文章http://javacrazyer.iteye.com/blog/790834中controller中的

    errors.reject("ccc", "用户名或密码有误!");
       return new ModelAndView(getFormView(), errors.getModel());

    它调用了BindException的reject方法,这样,再调用BindException的getModel()方法,就把错误连同表单等信息一并返回到表单页面用以显示。

    reject方法的第一个参数是错误码,如果设定了国际化资源,则显示资源文件中该错误码对应的错误条目,如果没有设定了国际化资源,则显示reject方法的第二个参数。

    reject方法的不足之处是在表现层不能区分错误消息属于那个字段,即不能说明是username不对呢还是password不对。解决这种情况可以使用rejectValue方法,这也是更一般使用的方法。rejectValue方法定义如下:

    rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage)


    第一个参数指定表单的域,即username或password,这样就可以分辨到底是那块出了问题;第二个参数和reject方法的第一个参数一样,制定错误码;第三个参选数制定了资源文件中的占位符;第四个参数和和reject方法的第二个参数一样。rejectValue方法还有一个简化的定义

    rejectValue(String field, String errorCode, String defaultMessage)


    上面是在Controller里使用的方法,使用上述方法后,若果出现BindException错误,返回表单页面时就会显示错误信息,那么如何在页面里显示错误信息呢?

    上面/WEB-INF/jsp/login.jsp里由于在controller里使用的是reject方法,所以只能那么显示,如果我们使用rejectValue方法,例如改动LoginController:

    errors.rejectValue("userName", "nameErr", null, "用户名错误");
       errors.rejectValue("password", "passErr", null, "密码错误");

    这样,就可以把页面改为如之前最后的形式

     <spring:bind path="command.userName"> 
            名称 <input type="text" name="${status.expression}" value="${status.value}"/>
                <font color="red"><c:out value="${status.errorMessage}" /></font><br/>
     </spring:bind> 
     <spring:bind path="command.password"> 
            密码 <input type="password" name="${status.expression}" value="${status.value}"/>
             <font color="red"><c:out value="${status.errorMessage}" /></font><br/>
       </spring:bind>

    这样错误的消息就绑定到相应的字段了。当然也可以不制定某个字段,一股脑都输出

    到此,大家还是没看到我是怎样讲解command这个值的,至于为什么非要是command而不是其他值
    这是因为setCommandClass这个方法是AbstractController中的一个方法,而这个
    方法使用到的一个默认值public static final java.lang.String DEFAULT_COMMAND_NAME = "command";public static final java.lang.String DEFAULT_COMMAND_NAME = "command";
    看到了没有,就是叫做command,所以在标签中就敢大胆的用啦 

     

    3.最后还有几个小问题

    (1)一个常见的错误:
    不通过controller直接访问含有spring:bind标签的JSP页面会出现下面的错误:
    javax.servlet.ServletException: Neither Errors instance nor plain target object for bean name 'person' available as request attribute 
    解决办法:
    http://spring.jactiongroup.net/viewtopic.php?p=5482

    (2)星号(*)的意思
    global and all field errors,
        ## use wildcard (*) in place of the property name
        <spring:bind path="company.*">
            <c:forEach items="${status.errorMessages}" var="error">
           c:out value="${error}"/><br/>
            </c:forEach>
        </spring:bind>

    (3)command的意思
       3-1 commandClass
       相当于struts中的ActionForm,用来封装V中的数据,方便在C中使用。

    3-2 commandName
       用来指定JSP中的数据需要绑定到哪个对象。默认为command
       比如下面的配置中,commandName就是command
         <spring:bind path='command.email'>
         <td><input type='text' name='${status.expression}' 
                    value='${status.value}' size='30' 
                    maxlength='100'></td></tr>
         </spring:bind>
    因为是缺省值,所以它就不需要再在Controller中显示声明

    如果在Controller中设置了setCommandName("me");则上面的配置文件需要改为:
         <spring:bind path='me.email'>
         <td><input type='text' name='${status.expression}' 
                    value='${status.value}' size='30' 
                    maxlength='100'></td></tr>
         </spring:bind>
    简单吧。


    (4)一个要注意的问题(原文链接)


    一个普通的<spring.bind>的使用类似于:

    <spring:bind path="user.age">
          <input type="text" name="age" value="${status.value}">
          <font color="red">${status.errorMessage}</font>
    </spring:bind>

    需要注意的是:<input>的name属性值必须与<spring:bind>的path属性的匹配,否则就绑定不了!

    例如下面的代码就绑定不了

    <spring:bind path="user.age">
          <input type="text" name="theAge" value="${status.value}">
          <font color="red">${status.errorMessage}</font>
    </spring:bind>

    为了避免手误,强烈推荐下列方法来绑定:

    <spring:bind path="user.age">
          <input type="text" name="${status.expression}" value="${status.value}">
          <font color="red">${status.errorMessage}</font>
    </spring:bind>

  • 相关阅读:
    Golang里边的map变量是什么?
    Golang map的底层实现
    方法和函数的区别
    Golang 对 对象和指针 的理解
    React 部分
    前端开发概述、html、css基础
    服务器核心知识
    常用模块8.7
    2017.8.2迭代器和生成器
    2017.7.18可变/不可变类型,符号运算及其流程控制
  • 原文地址:https://www.cnblogs.com/zhaoxd/p/3077375.html
Copyright © 2020-2023  润新知