当前位置: 移动技术网 > IT编程>开发语言>Java > Spring MVC深入学习

Spring MVC深入学习

2019年02月20日  | 移动技术网IT编程  | 我要评论

一、mvc思想

  • mvc思想简介:
       mvc并不是java所特有的设计思想,也不是web应用所特有的思想,它是所有面向对象程序设计语言都应该遵守的规范;mvc思想将一个应用部分分成三个基本部分:model(模型)、view(视图)和controller(控制器),这三个部分以最少的耦合协同工作,从而提高应用的可扩展性和可维护性;
  • mvc特点:
    • 多视图对应一个模型。按mvc模式,一个模型对应多个视图,可以减少代码的复制和维护量,这样一旦模式改变,易于维护;
    • 模型返回的数据和显示的逻辑分离。模型数据可以使用任何的显示技术,例如jsp、velocity模板或直接产生excel文档等;
    • 应用被分割为三层,降低了各层之间的耦合,提高了应用的可扩展性;
    • 控制层的概念很有效,他把不同的模型和不同的视图组合在一起,完成不同的请求。因此,控制层可以说包含了用户请求权限的概念;
    • mvc更符合软件工程化管理的精神,每一层的组件具有相似的特点,有利于通过工程化和工具化的方法产生管理程序代码;

二、springmvc的前端控制器dispatcherservlet

在许多的mvc框架中,都包含一个用于调度控制的servlet。spring mvc也提供了一个名为org.springframework.web.servlet.dispatcherservlet的servlet充当前端控制器,所有的请求驱动都围绕这个dispatcherservlet来分派请求。

dispatcherservlet是一个servlet(继承自httpservlet基类),在使用时需要把他配置到web.xml文件中,配置信息如下:

<servlet>
    <!-- servlet的名称 -->
    <servlet-name>springmvc</servlet-name>
    <!-- servlet对应的java类 -->
    <servlet-class>
        org.springframework.web.servlet.dispatcherservlet
    </servlet-class>
    
    <!-- 当前servlet的参数信息 -->
    <init-param>
        <!-- contextconfiglocation是参数名称,该参数的值包含springmvc的核心配置文件路径-->
        <param-name>contextconfiglocation</param-name>
        <param-value>/web-inf/springmvc-config.xml</param-value>
    </init-param>

    <!-- 在tomcat启动时立即加载servlet -->
    <load-on-startup>1</load-on-startup>
</servlet>
<!-- servlet映射声明 -->
<servlet-mapping>
    <!-- 请求对用的servlet名称 -->
    <servlet-name>springmvc</servlet-name>
    <!-- 监听当前所有请求 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

三、springmvc的后端控制器handle(两种配置方式)

  • 实现controller接口
  • 注解(重点)

   dispatcherservlet在spring当中充当前端控制器的角色,他的核心功能是分发请求,请求会被分发给对应处理的java类,spring mvc 中称为handle;controller接口的实现类只能处理一个单一请求动作,而spring2.5之后新增的基于注解的控制器可以支持同时处理多个请求动作,并且无需实现任何接口,更加灵活

四、spring mvc注解

  • @controller注解

        org.springframework.stereotype.controller注解类型用于指示spring类的实例是一个控制器,@controller用于标记一个类,使用它标记的类就是一个springmvc controller对象,即一个控制器类spring使用扫描机制查找应用程序中所有基于注解的控制器类。分发处理器会扫描使用了该注解的类的方法,并检测该方法是否使用了@requestmapping注解,使用了的方法才是真正处理请求的处理器。
 
        保证spring能找到控制器,需完成两件事:
    • 在springmvc核心配置文件头文件引入spring-context;
    • 使用<context:component-scan/>开启注解扫描(扫描被@controller、@service、@repository、@component等注解的类);
  • @requestmapping注解

   org.springframework.web.bind.annotation.requestmapping注解类型指示spring用哪一个类或方法来请求动作,该注解可用于类或方法
 
        @requestmapping可以用来注释一个控制器类,在这种情况下,所有方法都将映射为相对于类级别的请求,如下:
@controller
@requestmapping(value="/user")
public class usercontroller{
       @requestmapping(value="/register")
        public string register(){
            return "register";
        }
        @requestmapping(value="/login")
        public string login(){
            return "login";
        }
}   

由于usercontroller类中加了value="/user"的@requestmapping注解,因此所有相关路径都要加上“/user”,此时方法被映射到如下请求:

    • @requestmapping注解支持的常用属性
      • value属性:是@requestmapping注解默认属性,若只有唯一属性,则可省略属性名
                      @requestmapping(value="/hello")= @requestmapping("/hello")
      • method属性:表明该方法仅处理的http请求方式
                    @requestmapping(value="/hello",method=requestmethod.post),只支持post;如果没有指定method属性,则方法可以处理任意http请求方式
      • consumes属性:表明处理请求的提交内容类型;
      • produces属性:表明返回的内容类型;
      • params属性:指定request中必须包含某些参数时,才让该方法处理;
      • headers属性:指定request中必须包含某些指定的header值,才让该方法处理请求;
    • 请求处理方法可以出现的参数类型(springmvc默认支持的参数,可出现可不出现,取决于是否用到)_spring+mybatis企业应用实战p30
      • httpservletrequest对象
      • httpservletresponse对象
      • httpsession对象
      • model对象
      • modelmap对象
      • ......
  • @requestparam注解

        org.springframework.web.bind.annotation.requestparam注解类型用于将指定的请求参数赋值给方法中的形参
    • @requestparam注解支持的常用属性
      • name:string类型;指定请求头绑定的名称
      • value:string类型;name属性的别名
      • required:boolean类型;指定参数是否必须绑定(默认值为true);
      • defaultvalue:string类型;如果没有传递参数而是用的默认值
@requestmapping(value="/login")
public modelandview login(@requestparam("loginname") string loginname,
                          @requestparam("password") string password){
        return ......;
}
  • @pathvariable注解

        org.springframework.web.bind.annotation.pathvariable注解类型可以非常方便地获得请求url的中的动态参数;
    • @pathvariable注解支持属性
      • value:string类型,表示绑定的名称,若省略则默认绑定同名参数
@requestmapping(value="/pathvariabletest/{userid}")
public void pathvariabletest(@pathvariable integer userid)

假如请求的url为“http://localhost:8080/databindingtest/pathvariabletest/1”,则自动将url中的模板变量{userid}绑定到通过@pathvariable注解的同名参数上,即userid变量将被赋值为1。

  • @requestheader注解

        org.springframework.web.bind.annotation.requestheader注解类型用于将请求的头信息区数据映射到功能处理方法的参数上
    • @requestheader注解支持的属性
      • name:string类型;指定请求头绑定的名称
      • value:string类型;name属性的别名
      • required:boolean类型;指定参数是否必须绑定;
      • defaultvalue:string类型;如果没有传递参数而是用的默认值
@requestmapping(value="/requestheadertest")
public void requestheadertest(@requestheader("user-agent") string useragent,
                              @requestheader(value="accept") string[] accepts)

以上配置自动将请求头“user-agent”的值赋到useragent变量上,并将“accept”请求头的值赋到accepts参数上

  • @cookievalue注解

        org.springframework.web.bind.annotation.cookievalue用于将请求的cookie数据映射到功能处理方法的参数上
    • @cookievalue注解支持的属性
      • name:string类型;指定请求头绑定的名称
      • value:string类型;name属性的别名
      • required:boolean类型;指定参数是否必须绑定;
      • defaultvalue:string类型;如果没有传递参数而是用的默认值
@requestmapping(value="/cookievaluetest")
public void cookievaluetest(@cookievalue(value="jsessionid",defaultvalue="") string sessionid)

以上配置会自动将jsessionid值设置到sessionid参数上,defaultvalue表示cookie中没有jsessionid时默认为空

  • @sessionattributes注解(只能声明类,不能声明方法)

        org.springframework.web.bind.annotation.sessionattributes注解类型允许我们有选择的指定model中的哪些属性需要转存到httpsession当中
    • @sessionattributes注解支持的属性
      • names属性:string[]类型,model中属性名称,即存储在httpsession当中的属性名称;
      • value属性:string[]类型,names属性的别名;
      • types属性:class<?>[]类型,用来指定放入httpsession当中的对象类型
@controller
//将model中属性名为user的属性放入httpsession对象当中
@sessionattributes("user")
public class sessionattributescontroller{
    
    @requestmapping(value="/login")
    public string login(@requestparam("loginname") string loginname,
                        @requestparam("password") string password,
                        model model){
         //创建user对象,装载用户信息
         user user = new user();
         user.setloginname(loginname);
         user.setpassword(password);
         //将user对象添加到model当中
         model.addattribute("user",user);
         return "welcome";
    }
}

jsp页面测试以上@sessionattributes注解:

${requestscope.user.username}//页面显示admin
${sessionscope.user.username}//页面显示admin

结论:user对象被成功设置到了httpsession对象当中

@sessionattributes注解如下写法: @sessionattributes(types={user.class},value="user") 

还可以设置多个对象到httpsession当中:

  • @modelattribute注解

        org.springframe.web.bind.annotation.modelattribute注解类型将请求参数绑定到model对象
    • @modelattribute注解支持的属性(仅一个)
      • value属性:string类型,表示绑定的属性名称
        
    notice被@modelattribute注释的方法会在cobtroller每个方法执行前被执行,因此在一个controller映射到多个url时,要谨慎使用!
    @modelattribute注解使用情况:
    • @modelattribute(value="")注释返回具体类的方法@modelattribute("loginname") public string usermodel1(@requestparam("logginname") string loginname){ return loginname; }
    • @modelattribute注释void返回值的方法
      @modelattribute
      public void usermodel2(@requestparam("loginname") string loginname,
                             @requestparam("password") string password,
                             model model){
          model.addattribute("loginname",loginname);
          model.addattribute("password",password);
      }
    • @modelattribute注释返回具体类的方法
      @modelattribute
      public user usermodel3(@requestparam("loginname") string loginname,
                             @requestparam("password") string password){
           return new user(loginname,password);
      }
    • @modelattribute和@requestmapping同时注释一个方法@requestmapping(value="/login") @modelattribute(value="username") public string login4(){ return "admin"; }
    • @modelattribute注释一个方法的参数
      @modelattribute("user")
      public user usermodel5(@requestparam("loginname") string loginname,
                             @requestparam("password") string password){
          user user = new user();
          user.setloginname(loginname);
          user.setpassword(password);
          return user;
      }
      @requestmapping(value="/login5")
      public string login5(@modelattribute("user") user user){
          user.setusername("管理员");
          return "result5";
      }

      不管@modelattribute注解在什么情况下使用,model对象中的属性名都是@modelattribute的属性value值(有value存在情况下)或返回类型对象名(没有value存在情况下);model对象中的值是方法返回值(有返回值情况下)或自己填充model(没有返回值情况下)

五、springmvc框架乱码解决方案

  • post提交中文乱码解决

     在web.xml中添加过滤器:
<filter>
    <filter-name>characterencodingfilter</filter-name>
    <filter-class>org.springframework.web.filter.characterencodingfilter</filter-class>
       <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
       </init-param>
</filter>
<filter-mapping>
     <filter-name>characterencodingfilter</filter-name>
     <url-pattern>/*</url-pattern>
</filter-mapping>
  • get请求中文乱码解决(两种方法)

    • 修改tomcat配置文件添加编码与工程编码一致,如下:
      <connector uriencoding="utf-8" connectiontimeout="20000" port="8080" protocol="http/1.1" redirectport="8443"/>
    • 对参数进行重新编码:string username = new string(request.getparamter("username").getbytes("iso8859-1"),"utf-8")iso8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码

六、springmvc框架运转在执行handler(controller)时,spring会额外做一些工作:

  • 消息转换:将请求信息(如json、xml等数据)封装成一个对象,将对象转换为指定的相应信息;
  • 数据转换:对请求信息进行数据转换,如string转换成integer、double等;
  • 数据格式化:如将字符串转化成格式化数字或格式化日期;
  • 数据验证:验证数据的有效性(长度、格式等),验证结果存储到bindingresult或error中;
 
  • 特殊情况(解决日期类型参数转换问题):由于日期数据有很多种格式,所以springmvc没办法把字符串转换成日期类型。所以需要自定义参数绑定。前端控制器接收到请求后,找到注解形式的处理器适配器,对requestmapping标记的方法进行适配,并对方法中的形参进行参数绑定。在springmvc这可以在处理器适配器上自定义converter进行参数绑定。如果使用<mvc:annotation-driven/>可以在此标签上进行扩展。

     解决方案:

      自定义转换器converter

public class dateconverter implements converter<string, date> {
      @override
      public date convert(string source) {
           simpledateformat simpledateformat = new simpledateformat("yyyy-mm-dd hh:mm:ss");
           try {
                 return simpledateformat.parse(source);
           } catch (parseexception e) {
                 e.printstacktrace();
           }
           return null;
      }
}

      配置converter(在springmvc核心配置文件中)——第一种

<!-- 加载注解驱动 -->
      <mvc:annotation-driven conversion-service="conversionservice"/>
      <!-- 转换器配置 -->
      <bean id="conversionservice"
           class="org.springframework.format.support.formattingconversionservicefactorybean">
           <property name="converters">
                 <set>
                      <!-- 自己配置的日期转换器,如果还有其他转换器,可以继续在这里添加bean -->
                      <bean class="cn.itcast.spring mvc.convert.dateconverter"/>
                 </set>
           </property>
      </bean>

      第二种配置方式:此方法需要独立配置处理器映射器、适配器,不再使用<mvc:annotation-driven/>

<?xmlversion="1.0"encoding="utf-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"xmlns:p="http://www.springframework.org/schema/p"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xmlns:mvc="http://www.springframework.org/schema/mvc"
      xsi:schemalocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd">
      <!-- 扫描带controller注解的类 -->
      <context:component-scanbase-package="cn.itcast.springmvc.controller"/>
     
      <!-- 转换器配置 -->
      <beanid="conversionservice"
           class="org.springframework.format.support.formattingconversionservicefactorybean">
           <propertyname="converters">
                 <set>
                      <beanclass="cn.itcast.springmvc.convert.dateconverter"/>
                 </set>
           </property>
      </bean>

      <!-- 自定义webbinder -->
      <beanid="custombinder" class="org.springframework.web.bind.support.configurablewebbindinginitializer">
           <propertyname="conversionservice"ref="conversionservice"/>
      </bean>

      <!--注解适配器 -->
      <beanclass="org.springframework.web.servlet.mvc.method.annotation.requestmappinghandleradapter">
           <propertyname="webbindinginitializer"ref="custombinder"></property>
      </bean>

      <!-- 注解处理器映射器 -->
      <beanclass="org.springframework.web.servlet.mvc.method.annotation.requestmappinghandlermapping"/>
      
      <!-- 加载注解驱动 -->
      <!-- <mvc:annotation-driven/> -->

      <!-- 视图解析器 -->
      <beanclass="org.springframework.web.servlet.view.internalresourceviewresolver">
           <propertyname="viewclass"
                 value="org.springframework.web.servlet.view.jstlview"/>
           <!-- jsp前缀 -->
           <propertyname="prefix"value="/web-inf/jsp/"/>
           <!-- jsp后缀 -->
           <propertyname="suffix"value=".jsp"/>
      </bean>
</beans>

 七、消息转换:将json、xml消息封装成对象,转换为相应的响应信息

  • @requestbody注解

        org.springframework.web.bind.annotation.requestbody注解用于读取request请求的body部分数据,使用系统默认配置的httpmessageconverter进行解析,然后把相应的数据绑定到controller中方法的参数上;
        前台页面get或post提交数据时,数据编码格式由请求头的contenttype制定,可分以下情况:
    • application/x-www-form-urlencoded,这种情况的数据@requestparam、@modelattribute也可以处理,@requestbody也能处理(name1=value1&name2=value2........)
    • multipart/form-data,@requestbody不能处理这种数据(有file处理时)
    • application/json、application/xml等格式数据,必须用@requestbody处理     
  • 转换json数据

      spring mvc提供了处理json格式请求/响应的httpmessageconverter:mappingjackson2httpmessageconverter
   利用jackson开源类包处理json格式的请求和响应消息(spring官方说明,spring mvc默认使用mappingjackson2httpmessageconverter转换json格式数据,jackson开源类包可将java对象转换成json对象和xml文档,或逆转)
    • 示例:接收json格式的数据

       jsp页面:

<%@ page language="java" contenttype="text/html;charset=utf-8"
    pageencoding="utf-8"%>
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>测试接收json格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">

$(document).ready(function(){
    testrequestbody();
});

function testrequestbody(){
    $.ajax("${pagecontext.request.contextpath}/json/testrequestbody"//请求url
        {datatype:"json",    //预期服务器返回的数据类型
             type:"post",    //请求方式post或get
         contenttype:"application/json"  //发送信息至服务器时的内容编码格式
         //发送到服务器的数据
         data:json.stringify({id:1,name:"spring mvc企业应用实战"}),
         async:true,//默认设置下,所有请求为异步,若false,则发送同步请求
         //请求成功后的回掉函数
         success:function(data){
            console.log(data);
            $("#id").html(data.id);
            $("#name").html(data.name);
            $("#author").html(data.author);
          },
          //请求出错时调用的函数
          error:function(){
             alert("数据发送失败");
          }
    });
}
</script>
</head>
<body>
编号:<span id="id"></apan>

书名:<span id="name"></span>

作者:<span id="author></span>

</body>
</html>

      controller:

@controller
@requestmapping("/json")
public class bookcontroller{
    private static final log logger = logfactory.getlog(bookcontroller.class);
    //@requestbody根据json数据,转换成对应的object
    @requestmapping(value="/testrequestbody")
    public void setjson(@requestbody book book,
                        httpservletresponse response) throws exception{
        //objectmapper类是jackson库的主要类。他提供一些功能将java对象转换成对应的json格式数据
        objectmapper mapper = new objectmapper();
        //将book对象转换成为json输出
        logger.info(mapper.writevalueasstring(book));
          book.setauthor("肖文姬");
        response.setcontenttype("text/html;charset=utf-8");
        //将book对象转换成json写出到客户端
        response.getwriter().println(mapper.writevalueasstring(book));
    }
} 

setjson方法的第一个参数@requestbody book book表示,使用@requestbody注解获取到json数据后,将json数据设置到对应的book对象属性中去,参数response用于输出响应数据到客户端。

      book类:定义三个属性(id、name、author)

      springmvc核心配置文件:

<!-- 注解扫描 -->
<context:component-scan base-package="org.fkit.controller"/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>

<!-- 使用默认的servlet来响应静态文件 -->
<mvc:default-servlet-handler/>

<!-- 视图解析器 -->
<bean id="viewresoler" class="org.springframework.web.servlet.view.internalresourceviewresoler">
    <!-- 前缀 -->
    <property name="prefix">
        <value>/web-inf/content/</value>
    </property>
    <!-- 后缀 -->
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

<mvc:default-servlet-handler/>使用默认的servlet来响应静态文件,因为在web.xml文件中使用了dispatcherservlet截获所有请求url,而引入<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>的时候,dispatcherservlet会将“/”看成请求路径,找不到他的时候会报404错误。而当配置文件加上这个默认的servlet时,servlet在找不到它时会去找静态的内容,即js目录

    • 示例:自定义httpmessageconverter接收json格式数据(不使用默认jackson开源类包):说明:使用其他开源类包配置httpmessageconverter(fastjson)

      controller:

@controller
@requestmapping("/json")
public class bookcontroller{
    private static final log logger = logfactory.getlog(bookcontroller.class);
    //@requestbody根据json数据,转换成对应的object
    @requestmapping(value="/testrequestbody")
    public void setjson(@requestbody book book,
                        httpservletresponse response) throws exception{
        //jsonobject-lib包是一个beans,collections,maps,java arrays和xml和json互相转换的包

        //使用jsonobject将book对象转换成为json输出
        logger.info(jsonobject.tojsonstring(book));
        book.setauthor("肖文姬");
        response.setcontenttype("text/html;charset=utf-8");
        //将book对象转换成json写出到客户端
        response.getwriter().println(jsonobject.tojsonstring(book));
    }
}    

      springmvc-config.xml(核心配置文件):

<!-- 注解扫描 -->
<context:component-scan base-package="org.fkit.controller"/>

<!-- 注解驱动,摒弃默认配置httpmessageconverter,自定义配置httpmessageconverter -->
<mvc:annotation-driven>
    
    <!-- 设置不使用默认的消息转换器 -->
    <mvc:message-converter register-defaults="false">
        <!-- 配置spring的转换器 -->
        <bean class="org.springframework.http.converter.stringhttpmessageconverter"/>
        <bean class="org.springframework.http.converter.xml.xmlawareformhttpmessageconverter"/>
        <bean class="org.springframework.http.converter.bytearrayhttpmessageconverter"/>
        <bean class="org.springframework.http.converter.bufferedimagehttpmessageconverter"/>

        <!-- 配置fastjson中实现httpmessageconverter接口的转换器 -->
        <!-- fastjsonhttpmessageconverter是fastjson中实现了httpmessageconverter接口的类 -->
        <bean id="fastjsonhttpmessageconverter" class="com.alibaba.fastjson.support.spring.fastjsonhttpmessageconverter">
              <!-- 加入支持的媒体类型;返回contenttype -->
              <property name="supportedmediatypes">
                    <list>
                        <!-- 这里顺序不能反,一定先写text/html,不然ie下会出现下载提示 -->
                        <value>text/html;charset=utf-8</value>
                        <value>application/json;charset=utf-8</value>
                    </list>
              </property>
        </bean>
    </mvc:message-converter>

</mvc:annotation-driver>

<!-- 使用默认的servlet来响应静态文件 -->
<mvc:default-servlet-handler/>

<!-- 视图解析器 -->
<bean id="viewresoler" class="org.springframework.web.servlet.view.internalresourceviewresoler">
    <!-- 前缀 -->
    <property name="prefix">
        <value>/web-inf/content/</value>
    </property>
    <!-- 后缀 -->
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

本项目说明:自定义实现httpmessageconverter时,关闭默认使用的mappingjackson2httpmessageconverter消息转换器,不使用jackson开源类包,而是选择了更好的fastjson包,把转换器配置成fastjsonhttpmessageconverter类型,这个类实现了httpmessageconverter接口;

 

jsp页面和java文件同上一个项目;

总结:由上两个项目可知:处理json格式的开源类包使用jackson和fastjson,只是httpmessageconverter的实现不同;

  • @responsebody注解

         org.springframework.web.bind.annotation.responsebody注解用于将controller的方法返回的对象,通过适当的消息转换器转为指定格式后,写入response对象的body数据区。通常当返回的数据不是html标签页面,而是其他某种格式的数据时(如json、xml等)使用它。
    • 示例:返回json格式数据

      controller:

@controller
@requestmapping("/json")
public class bookcontroller{
    @requestmapping(value="/testresponsebody")
    //@responsebody会将集合数据转换为json格式并将其返回客户端
    @responsebody
    public object getjson(){
        list<book> list = new arraylist<book>();
        list.add(new book(1,"spring mvc 企业应用之战","肖文姬"));
        list.add(new book(2,"轻量级javaee企业应用之战","李刚"));
        return list;
    }
}

      jsp页面:

<%@ page language="java" contenttype="text/html;charset=utf-8"
    pageencoding="utf-8"%>
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>测试返回json格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">

$(document).ready(function(){
    testresponsebody();
});

function testresponsebody(){
    $.post("${pagecontext.request.contextpath}/json/testresponsebody",null,
            function(data){
                $.each(data,function(){
                    var tr = $("<tr align='center'/>");
                    $("<td/>").html(this.id).appendto(tr);
                    $("<td/>").html(this.name).appendto(tr);
                    $("<td/>").html(this.author).appendto(tr);
                    $("#booktable").append(tr);
                })
    },"json");
}
</script>
</head>
<body>
<table id="booktable" border="1" style="border-collapse:collapse;">
    <tr align="center">
        <th>编号</th>
        <th>书名</th>
        <th>作者</th>
    </tr>
</table>
</body>
</html>
  • 转换xml格式数据

       spring mvc提供了处理xml格式请求/响应的httpmessageconverter,如jaxb2rootelementhttpmessageconverter通过jaxb2读写xml消息,并将消息转换到注解@xmlrootelement和@xmltype作用的类中;
       spring官方说明,spring mvc默认使用了jaxb2rootelementhttpmessageconverter转换xml格式数据,jaxb(java architecture for xml binding)可以将json和xml互转;
       jaxb是一项可以根据xml schema产生java类的技术,在过程中,jaxb提供了将xml实例文档反向生成java对象的方法,并能将java对象的内容重新写到xml文档中。
       jaxb常用注解:@xmlrootelement、@xmlelement,等等
 
    • 示例:接收xml格式的数据

      jsp页面:

<%@ page language="java" contenttype="text/html;charset=utf-8" pageencoding="utf-8"%>
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>测试接收xml格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">

$(document).ready(function(){
    sendxml();
});

function sendxml(){
    var xmldata = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?><book><id>1</id><name>疯狂java 讲义</name><author>李刚</author></book>";
    $.ajax("${pagecontext.request.contextpath}/sendxml",//发送请求的url字符串
            {
                type:"post",//请求方式post或get
                contenttype:"application/xml",//发送信息至服务器时的内容编码类型
                //发送到服务器的数据
                data:xmldata,
                async:true,//默认设置下,异步请求;若false则发送同步请求
            });
}
</script>
</head>
<body>
</body>
</html>

      book类:

//@xmlrootelement表示xml文档的根元素
@xmlrootelement
public class book implements serializable{
    private integer id;
    private string name;
    private string author;
    
    public book(){
        super();
    }
    public book(integer id,string name,string author){
        this.id=id;
        this.name=name;
        this.author=author;
    }

    public integer getid(){
        return id;
    }
    //该属性作为xml的element
    @xmlelement
    public void setid(integer id){
        this.id=id;
    }
    public integer getname(){
        return name;
    }
    //该属性作为xml的element
    @xmlelement
    public void setname(string name){
        this.name=name;
    }
    public integer getauthor(){
        return author;
    }
    //该属性作为xml的element
    @xmlelement
    public void setauthor(string author){
        this.author =author ;
    }

    @override
    public string tostring(){
        return "book[id="+id+",name="+name+",author="+author+"]";
    }
}

      bookcontroller:

@controller
public class bookcontroller{
    private static final log logger = logfactory.getlog(bookcontroller.class);
    //@requestbody会自动将xml数据绑定到book对象
    @requestmapping(value="/sendxml,method=requestmethod.post")
    public void sendxml(@requestbody book book){
        logger.info(book);
        logger.info("接收xml数据成功");
    }
    
    //@responsebody会将book自动转换为xml数据返回
    @requestmapping(value="/readxml",method=requestmethod.post)
    public @responsebody book readxml() throws exception{
        //通过jaxbcontext的newinstance()方法,传递一个class就可以获得一个上下文
        jaxbcontext context = jaxbcontext.newinstance(book.class);
        
        //创建一个unmarshall对象
        unmarshaller unmar = context.createunmarshaller();
        inputstream is = this.getclass().getresourceasstream("/book.xml");

        //unmarshall对象的unmarshall方法可以进行xml到java对象的转换
        book book = (book)unmar.unmarshall(is);
        logger.info(book);
        return book;
    }
}

springmvc-config.xml核心配置文件中注解驱动<mvc:annotation-driven/>,默认配置了jaxb2rootelementhttpmessageconverter作为转换器处理xml数据转换

    • 示例:返回xml格式数据

      jsp页面:

<%@ page language="java" contenttype="text/html;charset=utf-8" 
    pageencoding="utf-8"%>
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>测试接收xml格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">

$(document).ready(function(){
    readxml();
});

function readxml(){
    $.ajax("${pagecontext.request.contextpath}/readxml",//发送请求的url字符串
            {
                datatype:"text",//预期服务器返回的数据类型
                type:"post",//请求方式post或get
                async:true,//默认设置下,异步请求;若false则发送同步请求
                success:function(xml){
                    //获得xml数据的id,name,author
                    var id=$("id",xml).text();
                    var name=$("name",xml).text();
                    var author=$("author",xml).text();
                    var tr = $("<tr align='center'/>");
                    $("<td/>").html(id).appendto(tr);
                    $("<td/>").html(name).appendto(tr);
                    $("<td/>").html(author).appendto(tr);
                    $("#booktable").append(tr);
                },
                //请求出错时调用的函数
                error:function(){
                    alert("数据接收失败");
                }
            });
}
</script>
</head>
<body>
<table id="booktable" border="1" style="border-collapse:collapse;">
    <tr align="center">
        <th>编号</th>
        <th>书名</th>
        <th>作者</th>
    </tr>
</table>
</body>
</html>

 八、数据绑定流程(把请求数据 安全的,没有任何问题的 赋值给处理方法参数)

spring mvc通过反射机制对目标处理方法的签名进行分析,并将请求信息绑定到处理方法的参数中。数据绑定的核心部件是databinder,其运行机制如下:

说明:spring mvc框架将servletrequest对象及处理方法的参数对象实例传递给databinder,databinder调用装配在spring web上下文中的conversionservice组件进行数据类型转换、数据格式化工作,并将servletrequest中的消息填充到参数对象。然后再调用validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验,并最终生成数据绑定结果bindingresult对象。bingdingresult对象包含已完成数据绑定的参数对象,还包含相应的校验错误对象,spring mvc抽取bindingresult中的参数对象及校验错误对象,将他们赋给处理方法的相应参数。

九、数据转换(两种方式)

  • propertyeditor接口(java所有)

    • 示例:使用@initbinder添加自定义编辑器转换数据(局部)

       spring mvc在支持新的转换器框架的同时,也支持javabean的propertyeditor

       自定义属性编辑器dateeditor:

//自定义属性编辑器
public class dateeditor extends propertyeditorsupport{
    //将传入的字符串数据转换成date类型
    @override
    public void setastext(string text) throws illegalargumentexception{
          simpledateformat dateformat = new simpledateformat("yyyy-mm-dd");
          try{
                date date = dateformat.parse(text);
                setvalue(date);
          }catch(parseexception e){
                e.printstacktrace();
          }
    }
}

       usercontroller:

//在控制器初始化时注册属性编辑器
@initbinder
public void initbinder(webdatabinder binder){                         
    //注册自定义编辑器                                           局部(仅在该控制器作用)
    binder.registercustomeditor(date.class,new dateeditor());
}
    • 示例:使用webbindinginitializer注册全局自定义编辑器转换数据

 

      datebindinginitializer:

//实现webbindinginitializer接口
public class datebindinginitializer implements webbindinginitializer{
    @override
    public void initbinder(webdatebinder binder,webrequest request){
        //注册自定义编辑器
        binder.registercustomeditor(date.class,new dateeditor());
    }
}

      usercontroller类不需要使用@initbinder注解的方法,而是在springmvc-config.xml配置文件中配置全局的自定义编辑器

      springmvc-config.xml:

<!-- 通过annotationmethodhandleradapter装配自定义编辑器 -->
<bean class="org.springframework.web.servlet.mvc.annotation.annotationmethodhandleradapter">
    <property name="webbindinginitializer">
        <bean class="org.fkjava.binding.datebindinginitializer"/>
    </property>
</bean>
  • conversionservice接口(spring所有)

    • org.springframework.core.convert.conversionservice是spring类型转换的核心接口,接口中有4个方法
    • spring在org.springframework.core.convert.converter包中定义了3种类型的转换器接口,使用时,只需实现其中任意一个接口,并将它作为自定义转换器注册到conversionservicefactorybean当中。
      • converter<s,t>接口:t  convert(s  source):将s类型的对象转换为t类型的对象(常用)
      • converterfactory<s,r>接口:说明:将一种类型的对象转换为另一种类型及其子类对象,比如将string转换为number以及number子类integer、double等对象,就需要一系列的converter,如                                            stringtointeger、stingtodouble等。此接口的作用就是将相同系列多个converter封装在一起。
        • <t   extends   r>   converters<s,t>    getconverter(class<t>  targettype)s为转换的源类型,r为目标类型的基类,t为r的子类。
      • genericconverter接口:略
    • 示例:使用conversionservice转换数据

      registerform.jsp页面

<h3>注册页面</h3>
<form action="register" method="post">
    <table>
        <tr>
            <td><label>登录名:</label></td>
            <td><input type="text" id="loginname" name="loginname"></td>
        </tr>
        <tr>
            <td><label>生日:</label></td>
            <td><input type="text" id="birthday" name="birthday"></td>
        </tr>
        <tr>
            <td><input type="submit" id="submit" value="登陆"></td>
        </tr>
    </table>
</form>   

      javabean——user

public class user implements serializable{
    private string loginname;
    private date birthday;
    public user(){super();} 
    ...省略seter/geter...       
}

说明:user用于接收jsp页面传入的loginname和birthday属性,而jsp页面传入的数据都是string类型,分析出须将string转换为date类型

      usercontroller

@controller
public class usercontroller{
    private static final log logger = logfactory.getlog(usercontroller.class);
    @requestmapping(value="/{formname}")
    public string loginform(@pathvariable string formname){
        //跳转动态页面
        return formname;
    }

    @requestmapping(value="/register",method=requestmethod.post)
    public string register(@modelattribute user user,model model){
        logger.info(user);
        model.addattribute("user",user);
        return "success";
    }
}

异常原因:此时,在页面输入数据,点击登陆,由于页面传入的是字符串数据“2017-12-29”,而处理方法中的参数user对象的birthday属性类型是date,因此此时会出现数据转换异常;

      自定义转换器,将传递的字符串转换成date类型

//实现converter<s,t>接口
public class stringtodateconverter implements converter<string,date>{
    //日期类型模版:如yyyy-mm-mm
    private string datepattern;
    public void setdatepattern(string datepattern){
        this.datepattern = datepattern;
    }
    //converter<s,t>接口的类型转换方法
    @override
    public date convert(string date){
        try{
            simpledateformat dateformat = new simpledateformat(this.datepattern);
            //将日期字符串转换为日期返回
            return dateformat.parse(date);
        }catch(exception e){
            e.printstacktrace();
            system.out.println("日期转换失败");
            return null;
        }
    }
}

      在springmvc-config.xml核心配置文件中加入自定义字符转换器

<!-- 装配自定义的类型转换器 -->
<mvc:annotation-driven conversion-service="convertionservice"/>
<!-- 自定义的类型转换器 -->
<bean id="conversionservice" class="org.springframework.context.support.conversionservicefactorybean">
    <property name="converters">
        <list>
            <bean class="org.fkit.converter.stringtodateconverter" p:datepattern="yyyy-mm-dd"/>
        </list>
    </property>
</bean>

说明:<mvc:annotation-driven/>标签还会注册一个默认的conversionservice,即formattingconversionservicefactorybean,以满足大多数类型转换的需求,现在需要注册一个自定义的stringtodateconverter转换类,因此,需要显示定义一个converterservice覆盖<mvc:annotation-driven/> 中的默认实现类,这一步需要通过设置converters属性完成

      success.jsp

<body>
登录名:${requestscope.user.loginname}//jack
生日:<fmt:formatdate value="${requestscope.user.birthday}" pattern="yyyy年mm月dd日"/>//2016年01月01日
</body>
  • 多种转换器的优先顺序

   @initbinder装配的局部自定义编辑器——>spring的conversionservice装配的自定义转换器 ——>webbinderinitializer接口装配的全局自定义编辑器

十、数据格式化

spring使用converter转换器进行源类型对象到目标类型对象的转换,spring转换器并不承担输入以及输出信息格式化的工作,在不同的本地化环境中,同一类型的数据还会相应的呈现不同的显示格式。

spring格式化框架:格式化数据(in)——>真正有用的数据——>格式化数据(out);位于org.springframework.format包,其中最重要的接口是formatter<t>接口;
  converter formatter
转换类型 任意object——>任意object 任意object——>string
适用范围 web层、service层、dao层 web层
建议
在spring mvc的应用程序当中,如果向转换表单中的用户输入,
则建议使用formatter,而不是converter

    

 

 

  • formatter格式转换是spring通过用的,定义在org.springframework.format包中,包中定义的接口有:

    • printer<t>接口:格式化接口,其中t类型对象根据locale信息以某种格式进行字符串格式输出
      • string print(t object,locale locale)
    • parser<t>接口:根绝locale信息解析字符串到t类型对象。
      • t parse(string text,locale locale) throws parseexception
    • formatter<t>接口:格式化接口,继承自printer<t>和parser<t>接口,可以完成t类型对象的格式化和解析功能;
    • formatterregistrar接口:注册格式化转换器。该接口定义了一个registerformatters方法,其参数是formatterregistry对象,用于注册多个格式化转换器。
      • void registerformatters(formatterregistry registry)
    • annotationformatterfactor<a extends annotation> y接口:略
  • 使用formatter格式化数据

   自定义格式化转换器dateformatter:

//实现formatter<t>接口
public class dateformatter implements formatter<date>{
    //日期类型模板:如:yyyy-mm-dd
    private string datepattern;
    //日期格式化对象
    private simpledateformat dateformat;
    //构造器,通过依赖注入的日期类型创建日期格式化对象
    public dateformatter(string datepattern){
        this.datepattern = datepattern;
        this.dateformat = new simpledateformat(datepattern);
    }

    //显示formatter<t>的t类型对象
    @override
    public string print(date date,locale locale){
        return dateformat.format(date);
    }

    //解析文本字符串,返回一个formatter<t>的t类型对象
    @override
    public date parse(string source,locale locale){
        try{
            return dateformat.parse(source);
        }catch(exception e){
            e.printstacktrace();
            throw new illegalargumentexception();
        }
    }
}

说明:dateformatter类实现了org.springframework.format.formatter接口,实现了接口中的两个方法:parse()和print();日期类型模板会通过配置文件的依赖注入设置

   在springmvc-config.xml中加入自定义的格式化转换器:

<!-- 装配自定义格式化转换器 -->
<mvc:annotation-driven conversion-service="conversionservice"/>

<!-- 格式化转换器 -->
<bean id="conversionservice"                                                                                       class="org.springframework.format.support.formattingconversionservicefactorybean">
    <property name="formatters">
        <list>
            <bean class="org.fkit.formatter.dateformatter" c:_0="yyyy-mm-dd"/>
        </list>
    </property>
</bean>

说明:以上配置使用formattingconversionservicefactorybean对自定义的格式转换器dateformatter进行了注册。formattingconversionservicefactorybean类有一个属性converters,可以用它注册converter;有一个属性formatters,可以用它注册formatter

以上使用实现formatter<t>接口的方式完成转换数据

 spring提供了很多常用的formatter实现。对于这些实现类,不需要自定义转换功能,只需要在springmvc-config.xml配置文件中配置好就能用:
  • 在org.springframework.format.datetime包中
    • dateformatter:用于时间对象格式化
  • 在org.springframework.format.number包中
    • numberformatter:用于数字类型对象格式化
    • currencyformatter:用于货币类型对象格式化
    • percentformatter:用于百分数数字类型对象格式化
例如:使用org.springframework.format.datetime包中的dateformatter完成字符串到日期对象的转化,如下配置即可:
在springmvc-config.xml中加入自定义的格式化转换器:
<!-- 装配自定义格式化转换器 -->
<mvc:annotation-driven conversion-service="conversionservice"/>

<!-- 格式化转换器 -->
<bean id="conversionservice" class="org.springframework.format.support.formattingconversionservicefactorybean">
    <property name="formatters">
        <list>
            <bean class="org.springframework.format.datetime.dateformatter" p:pattern="yyyy-mm-dd"/>
        </list>
    </property>
</bean>
  • 使用annotationformatterfactory<a extends annotation>格式化数据

   spring为开发者提供了注解驱动的属性对象格式化功能:在bean属性中设置、spring mvc处理方法参数绑定数据、模型数据输出时自动通过注解应用格式化的功能

   在org.springframework.format.annotation包下面定义了两个格式化的注解类型:

    • datetimeformat
           @datetimeformat注解可以对java.util.date、java.util.calendar等时间类型的属性进行标注;它支持一下几个互斥的属性。具体说明如下:
      • iso。类型为datetimeformat.iso。以下几个常用可选值:
        • datetimeformat.iso.date:格式为yyyy-mm-dd
        • datetimeformat.iso.date_time:格式为yyyy-mm-dd hh:mm:ss.sssz
        • datetimeformat.iso.time:格式为hh:mm:ss.sssz
        • datetimeformat.iso.none:表示不使用iso格式的时间
      • pattern。类型为string,使用自定义的时间格式化字符串,如“yyyy-mm-dd hh:mm:ss”
      • style。
    • numberformat
           @numberformat可对类似数字类型的属性进行标记,他有两个互斥属性,具体如下:

相关文章:

验证码:
移动技术网