首页 > 代码库 > Spring MVC 学习笔记(二)

Spring MVC 学习笔记(二)

6. 视图和视图解析器
 ?  Spring MVC如何解析视图
                              技术分享
 

    ? 请求处理方法执行完成后,最终返回一个ModelAndView对象。对于那些返回StringViewModeMap等类型的处理方法,spring MVC 都会在内部将它们装配成一个ModelAndView对象,它包含了逻辑名和模型对象的视图

    ? Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP也可是Excell

JFreeChart等各种表现形式的视图

? 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现MVC 的充分解耦

 
 ?  视图

    ? 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。

    ? 为了实现视图模型和具体实现技术的解耦,Spring 在org.springframework.web.servlet包中定义了一个高度抽象的View接口

                         技术分享

    ? 视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题

 
  常用的视图实现类
 
  技术分享
 
 ?  视图解析器(将逻辑视图转为物理视图)

    ? SpringMVC为逻辑视图名的解析提供了不同的策略,可以在Spring WEB上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。

    ? 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。

    ? 所有的视图解析器都必须实现ViewResolver接口:

                            技术分享
 
  常用的视图解析器实现类
 
技术分享

    ? 程序员可以选择一种视图解析器或混用多种视图解析器

 

    ? 每个视图解析器都实现了Ordered 接口并开放出一个order属性,可以通过order 属性指定解析器的优先顺序,order 越小优先级越高。

    ? SpringMVC会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,则抛出ServletException异常
 
 ?  InternalResourceViewResolver

 

    ? JSP 是最常见的视图技术,可以使用InternalResourceViewResolver作为视图解析器:

 
[java] view plain copy
 
 print?技术分享技术分享
  1. <!-- 配置视图解析器:如何把handler方法返回值解析为实际的物理视图-->    
  2. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  3.         <property name="prefix" value=http://www.mamicode.com/"/WEB-INF/views/"></property>  
  4.         <property name="suffix" value=http://www.mamicode.com/".jsp"></property>  
  5.     </bean>  
 

 

    ? 若项目中使用了JSTL,则SpringMVC会自动把视图由InternalResourceView转为JstlView

    ? 若使用JSTL fmt标签则需要在SpringMVC的配置文件中配置国际化资源文件

 
[html] view plain copy
 
 print?技术分享技术分享
  1. <!--  配置国际化资源文件 -->     
  2.      <bean id="messageSource"  
  3.             class="org.springframework.context.support.ResourceBundleMessageSource">  
  4.         <property name="basename" value=http://www.mamicode.com/"i18n"></property>      
  5.     </bean<span style="font-family:微软雅黑;background-color: inherit; line-height: 1.875;">     
  6. </span>  

    ? 若希望直接响应通过SpringMVC渲染的页面,可以使用mvc:view-controller标签实现

 
[html] view plain copy
 
 print?技术分享技术分享
  1. <!-- 配置直接转发的页面 -->      
  2. <!-- 可以直接相应转发的页面, 而无需再经过 Handler 的方法.  -->  
  3.     <mvc:view-controller path="/success" view-name="success"/>  
  4.   
  5.     <!-- 在实际开发中通常都需配置 mvc:annotation-driven 标签,  之前的页面才不会因为配置了直接转发页面而受到影响 -->  
  6.     <mvc:annotation-driven></mvc:annotation-driven>  
 
 
 ? Excel 视图

    ? 若希望使用Excel 展示数据列表,仅需要扩展SpringMVC提供的AbstractExcelViewAbstractJExcelView 即可。实现buildExcelDocument()方法,在方法中使用模型数据对象构建Excel 文档就可以了。

    ? AbstractExcelView基于POI API,而AbstractJExcelView是基于JExcelAPI的。

    ? 视图对象需要配置IOC 容器中的一个Bean,使用BeanNameViewResolver作为视图解析器即可

    ? 若希望直接在浏览器中直接下载Excel 文档,则可以设置响应头Content-Disposition的值为attachment;filename=xxx.xls

 
 ? 关于重定向

    ? 一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理

    ? 如果返回的字符串中带forward:redirect:前缀时,SpringMVC会对他们进行特殊处理:将forward: 和redirect: 当成指示符,其后的字符串作为URL 来处理

        – redirect:success.jsp:会完成一个到success.jsp的重定向的操作

        – forward:success.jsp:会完成一个到success.jsp的转发操作

 
[java] view plain copy
 
 print?技术分享技术分享
  1. @Controller  
  2. @RequestMapping("springmvc")  
  3. //@SessionAttributes(value=http://www.mamicode.com/{"user"},types={String.class})  
  4. public class SpringMVCTest {  
  5.     @RequestMapping(value=http://www.mamicode.com/"/testRedirect")  
  6.     public String testRedirect(){  
  7.         System.out.println("testRedirect...");  
  8.         return "redirect:/index2.jsp";       //如果是转发就写为forward:             
  9.     }}  
 
 
7. RESTful SpringMVC CRUD

    需求:

    ①.显示所有员工信息(url:emps     请求方式:GET    显示效果如下)
    技术分享
 ②. 添加员工信息
      显示添加页面(url:emp     请求方式:GET    添加页面显示效果如下)
    添加后页面(url:emp     请求方式:POST    完成添加后重定向到list页面)
 ③. 删除(url:emp/{id}     请求方式:DELETE    对应记录从数据表中删除)
 ④. 修改,lastName不可修改
      显示修改页面(url:emp/{id}     请求方式:GET    回显表单)
    添加后页面(url:emp     请求方式:PUT       完成修改后重定向到list页面)
 
   实现:
 
[java] view plain copy
 
 print?技术分享技术分享
  1. @Controller  
  2. public class EmployeeHandler {  
  3.   
  4.     @Autowired  
  5.     private EmployeeDao employeeDao;  
  6.     @Autowired  
  7.     private DepartmentDao departmentDao;  
  8.   
  9.     @RequestMapping("/emps")  
  10.     public String list(Map<String,Object> map){  
  11.         map.put("employees", employeeDao.getAll());  
  12.         return "list";  
  13.     }  
  14.   
  15.     @RequestMapping(value=http://www.mamicode.com/"emp",method=RequestMethod.GET)  
  16.     public String input(Map<String,Object> map){  
  17.         map.put("departments", departmentDao.getDepartments());  
  18.         map.put("employee", new Employee());  
  19.         return "input";  
  20.     }  
  21.   
  22.     @RequestMapping(value=http://www.mamicode.com/"/emp",method=RequestMethod.POST)  
  23.     public String save(Employee employee){  
  24.         employeeDao.save(employee);  
  25.         return "redirect:/emps";  
  26.     }  
  27.   
  28.     @RequestMapping(value=http://www.mamicode.com/"/emp/{id}",method=RequestMethod.DELETE)  
  29.     public String delete(@PathVariable("id") Integer id){  
  30.         employeeDao.delete(id);  
  31.         return "redirect:/emps";  
  32.     }  
  33.   
  34.     @RequestMapping(value=http://www.mamicode.com/"/emp/{id}", method=RequestMethod.GET)  
  35.     public String input(@PathVariable("id") Integer id, Map<String, Object> map){  
  36.         map.put("employee", employeeDao.get(id));  
  37.         map.put("departments", departmentDao.getDepartments());  
  38.         return "input";  
  39.     }  
  40.   
  41.     @RequestMapping(value=http://www.mamicode.com/"/emp", method=RequestMethod.PUT)  
  42.     public String update(Employee employee){  
  43.         employeeDao.save(employee);  
  44.         return "redirect:/emps";  
  45.     }  
  46.   
  47.     @ModelAttribute  
  48.     public void getEmployee(@RequestParam(value=http://www.mamicode.com/"id",required=false) Integer id,  
  49.             Map<String, Object> map){  
  50.         if(id != null){  
  51.             map.put("employee", employeeDao.get(id));  
  52.         }    
  53.   }    }  
  54.   
  55. ————————————web.xml——————————————————————  
  56. <?xml version="1.0" encoding="UTF-8"?>  
  57. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  58.     xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  59.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  60.     id="WebApp_ID" version="2.5">  
  61.     <!-- 配置 SpringMVC 的 DispatcherServlet -->  
  62.     <servlet>  
  63.         <servlet-name>springDispatcherServlet</servlet-name>  
  64.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  65.         <init-param>  
  66.             <param-name>contextConfigLocation</param-name>  
  67.             <param-value>classpath:springmvc.xml</param-value>  
  68.         </init-param>  
  69.         <load-on-startup>1</load-on-startup>  
  70.     </servlet>  
  71.   
  72.     <servlet-mapping>  
  73.         <servlet-name>springDispatcherServlet</servlet-name>  
  74.         <url-pattern>/</url-pattern>  
  75.     </servlet-mapping>  
  76.   
  77.     <!-- 把post转为delete请求和put请求 -->  
  78.     <filter>  
  79.         <filter-name>HiddenHttpMethodFilter</filter-name>  
  80.         <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>  
  81.     </filter>  
  82.   
  83.     <filter-mapping>  
  84.         <filter-name>HiddenHttpMethodFilter</filter-name>  
  85.         <url-pattern>/*</url-pattern>  
  86.     </filter-mapping>  
  87. </web-app>  
  88. ————————————springmvc.xml————————————————————  
  89. <?xml version="1.0" encoding="UTF-8"?>  
  90. <beans xmlns="http://www.springframework.org/schema/beans"  
  91.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
  92.     xmlns:mvc="http://www.springframework.org/schema/mvc"  
  93.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  94.         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
  95.         http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  
  96.   
  97.     <!-- 配置自动扫描的包 -->  
  98.     <context:component-scan base-package="com.starfish.springmvc"></context:component-scan>  
  99.   
  100.     <!-- 配置视图解析器 -->  
  101.     <!-- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"      
  102.         p:prefix="/WEB-INF/views/"    p:suffix=".jsp"/>  
  103.     </bean> -->  
  104.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  105.         <property name="prefix" value=http://www.mamicode.com/"/views/"></property>  
  106.         <property name="suffix" value=http://www.mamicode.com/".jsp"></property>  
  107.     </bean>  
  108.   
  109.     <mvc:default-servlet-handler/>  
  110.   
  111.     <mvc:annotation-driven></mvc:annotation-driven>  
  112. </beans>  
  113. ———————————————————list.jsp———————————————————————————  
  114. <html>  
  115. <head>  
  116. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  117. <title>Insert title here</title>  
  118. <!--    
  119.     SpringMVC 处理静态资源:  
  120.     1. 为什么会有这样的问题:  
  121.     优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀  
  122.     若将 DispatcherServlet 请求映射配置为 /,   
  123.     则 Spring MVC 将捕获 WEB 容器的所有请求, 包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理,   
  124.     因找不到对应处理器将导致错误。  
  125.     2. 解决: 在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>  
  126. -->  
  127. <script type="text/javascript" src=http://www.mamicode.com/"scripts/jquery-1.9.1.min.js"></script>  
  128. <script type="text/javascript">  
  129.     $(function(){  
  130.         $(".delete").click(function(){  
  131.             var href = $(this).attr("href");  
  132.             $("form").attr("action", href).submit();              
  133.             return false;  
  134.         });  
  135.     })  
  136. </script>  
  137. </head>  
  138. <body>  
  139.     <form action="" method="POST">  
  140.         <input type="hidden" name="_method" value=http://www.mamicode.com/"DELETE"/>  
  141.     </form>  
  142.   
  143.     <c:if test="${empty requestScope.employees }">  
  144.         没有任何员工信息.  
  145.     </c:if>  
  146.     <c:if test="${!empty requestScope.employees }">  
  147.         <table border="1" cellpadding="10" cellspacing="0">  
  148.             <tr>  
  149.                 <th>ID</th>  
  150.                 <th>LastName</th>  
  151.                 <th>Email</th>  
  152.                 <th>Gender</th>  
  153.                 <th>Department</th>  
  154.                 <th>Edit</th>  
  155.                 <th>Delete</th>  
  156.             </tr>  
  157.   
  158.             <c:forEach items="${requestScope.employees }" var="emp">  
  159.                 <tr>  
  160.                     <td>${emp.id }</td>  
  161.                     <td>${emp.lastName }</td>  
  162.                     <td>${emp.email }</td>  
  163.                     <td>${emp.gender == 0 ? ‘Female‘ : ‘Male‘ }</td>  
  164.                     <td>${emp.department.departmentName }</td>  
  165.                     <td><a href=http://www.mamicode.com/"emp/${emp.id}">Edit</a></td>  
  166.                     <td><a class="delete" href=http://www.mamicode.com/"emp/${emp.id}">Delete</a></td>  
  167.                 </tr>  
  168.             </c:forEach>  
  169.         </table>  
  170.     </c:if>  
  171.     <br><br>  
  172.     <a href=http://www.mamicode.com/"emp">Add New Employee</a>  
  173. </body>  
  174. </html>  
  175. ————————————————input.jsp————————————————————————————————  
  176. <body>  
  177.     <!--    
  178.         1. WHY 使用 form 标签呢 ?  
  179.         可以更快速的开发出表单页面, 而且可以更方便的进行表单值的回显  
  180.         2. 注意:  
  181.         可以通过 modelAttribute 属性指定绑定的模型属性,  
  182.         若没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean  
  183.         如果该属性值也不存在,则会发生错误。  
  184.     -->  
  185.     <form:form action="${pageContext.request.contextPath }/emp" method="POST"   
  186.         modelAttribute="employee">  
  187.         <form:errors path="*"></form:errors>  
  188.         <br>  
  189.         <c:if test="${employee.id == null }">  
  190.             <!-- path 属性对应 html 表单标签的 name 属性值 -->  
  191.             LastName: <form:input path="lastName"/>  
  192.             <form:errors path="lastName"></form:errors>  
  193.         </c:if>  
  194.         <c:if test="${employee.id != null }">  
  195.             <form:hidden path="id"/>  
  196.             <input type="hidden" name="_method" value=http://www.mamicode.com/"PUT"/>  
  197.             <%-- 对于 _method 不能使用 form:hidden 标签, 因为 modelAttribute 对应的 bean 中没有 _method 这个属性 --%>  
  198.             <%--   
  199.             form标签有表单回显的作用,所以form标签中的path属性必须在modelAttribute中的bean有相应的属性  
  200.                 <form:hidden path="_method" value=http://www.mamicode.com/"PUT"/>  这样写的话bean中必须有_method属性  
  201.             --%>  
  202.         </c:if>  
  203.         <br>  
  204.         Email: <form:input path="email"/>  
  205.         <form:errors path="email"></form:errors>  
  206.         <br>  
  207.         <%   
  208.             Map<String, String> genders = new HashMap();  
  209.             genders.put("1", "Male");  
  210.             genders.put("0", "Female");  
  211.             request.setAttribute("genders", genders);  
  212.         %>  
  213.         Gender:   
  214.         <br>  
  215.         <form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/>  
  216.         <br>  
  217.         Department: <form:select path="department.id"   
  218.             items="${departments }" itemLabel="departmentName" itemValue=http://www.mamicode.com/"id"></form:select>  
  219.         <br>  
  220.         <!--    
  221.             1. 数据类型转换  
  222.             2. 数据类型格式化  
  223.             3. 数据校验.   
  224.             1). 如何校验 ? 注解 ?  
  225.             ①. 使用 JSR 303 验证标准  
  226.             ②. 加入 hibernate validator 验证框架的 jar 包  
  227.             ③. 在 SpringMVC 配置文件中添加 <mvc:annotation-driven />  
  228.             ④. 需要在 bean 的属性上添加对应的注解  
  229.             ⑤. 在目标方法 bean 类型的前面添加 @Valid 注解  
  230.             2). 验证出错转向到哪一个页面 ?  
  231.             注意: 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参  
  232.             3). 错误消息 ? 如何显示, 如何把错误消息进行国际化  
  233.         -->  
  234.         <%-- Birth: <form:input path="birth"/>  
  235.         <form:errors path="birth"></form:errors>  
  236.         <br>  
  237.         Salary: <form:input path="salary"/> --%>  
  238.         <br>  
  239.         <input type="submit" value=http://www.mamicode.com/"Submit"/>  
  240.     </form:form>  
  241. </body>  
 
 
8.SpringMVC表单标签&处理静态资源
 
 
 ?  使用Spring 的表单标签

    ? 通过SpringMVC的表单标签可以实现将模型数据中的属性和HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显

 

 ?  form 标签

 
    ? 一般情况下,通过GET 请求获取表单页面,而通过POST 请求提交表单页面,因此获取表单页面和提交表单页面的URL 是相同的。只要满足该最佳条件的契约,<form:form> 标签就无需通过action 属性指定表单提交的URL
    ? 可以通过modelAttribute属性指定绑定的模型属性,若没有指定该属性,则默认从request 域对象中读取command的表单bean,如果该属性值也不存在,则会发生错误。
 
 
 ?  表单标签

    ? SpringMVC提供了多个表单组件标签,如<form:input/><form:select/>等,用以绑定表单字段的属性值,它们的共有属性如下:

        – path:表单字段,对应html 元素的name 属性,支持级联属性

        – htmlEscape:是否对表单值的HTML 特殊字符进行转换,默认值为true

        – cssClass:表单组件对应的CSS 样式类名

        – cssErrorClass:表单组件的数据存在错误时,采取的CSS 样式

 
 
    ? form:inputform:passwordform:hiddenform:textarea:对应HTML 表单text、passwordhidden、textarea标签
    ? form:radiobutton:单选框组件标签,当表单bean 对应的属性值和value 值相等时,单选框被选中
    ? form:radiobuttons:单选框组标签,用于构造多个单选框
           – items:可以是一个List、String[]或Map
           – itemValue:指定radio 的value 值。可以是集合中bean 的一个属性值
           – itemLabel:指定radio 的label 值
           – delimiter:多个单选框可以通过delimiter 指定分隔符  delimiter="<br>"
 
    ? form:checkbox:复选框组件。用于构造单个复选框
    ? form:checkboxs:用于构造多个复选框。使用方式同form:radiobuttons标签
    ? form:select:用于构造下拉框组件。使用方式同form:radiobuttons标签
    ? form:option:下拉框选项组件标签。使用方式同form:radiobuttons标签
    ? form:errors:显示表单组件或数据校验所对应的错误
        –  <form:errorspath=“*” />:显示表单所有的错误
        – <form:errorspath= “ user*” />:显示所有以user 为前缀的属性对应的错误
        – <form:errorspath= “ username”/>:显示特定表单对象属性的错误
 
 

 

 ?  处理静态资源

    ? 优雅的REST 风格的资源URL 不希望带.html 或.do 等后缀

    ? 若将DispatcherServlet请求映射配置为/,则Spring MVC 将捕获WEB 容器的所有请求,包括静态资源的请求,

SpringMVC会将他们当成一个普通请求处理,因找不到对应处理器将导致错误。

 

    ? 可以在SpringMVC的配置文件中配置<mvc:default-servlet-?handler/>的方式解决静态资源的问题:

        –  <mvc:default-servlet-handler/> 将在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler,它会对进入DispatcherServlet的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由WEB 应用服务器默认的Servlet 处理,如果不是静态资源的请求,才由DispatcherServlet继续处理

        –  一般WEB 应用服务器默认的Servlet 的名称都是default。若所使用的–WEB 服务器的默认Servlet 名称不是default,则需要通过default-servlet-name 属性显式指定

 
9.数据转换&数据格式化&数据校验
 
 数据绑定流程

    ? 1. Spring MVC主框架将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象

    ? 2. DataBinder调用装配在Spring MVC上下文中的ConversionService组件进行数据类型转换、数据格式化工作。将Servlet 中的请求信息填充到入参对象中

    ? 3. 调用Validator组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData对象

    ? 4. Spring MVC抽取BindingResult中的入参对象和校验错误对象,将它们赋给处理方法的响应入参

 

    ? Spring MVC通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder,运行机制如下:

                     技术分享

      技术分享

 ?  数据转换

    ? Spring MVC 上下文中内建了很多转换器,可完成大多数Java 类型的转换工作。

    ? ConversionServiceconverters = 

        – java.lang.Boolean  ->  java.lang.String:   

        org.springframework.core.convert.support.ObjectToStringConverter@f874ca

        – java.lang.Character-> java.lang.Number: CharacterToNumberFactory@f004c9

        – java.lang.Character-> java.lang.String: ObjectToStringConverter@68a961

        – java.lang.Enum-> java.lang.String: EnumToStringConverter@12f060a

        – java.lang.Number-> java.lang.Character: NumberToCharacterConverter@1482ac5

        – java.lang.Number-> java.lang.Number: NumberToNumberConverterFactory@126c6f

        – java.lang.Number-> java.lang.String: ObjectToStringConverter@14888e8

        – java.lang.String-> java.lang.Boolean: StringToBooleanConverter@1ca6626

        – java.lang.String-> java.lang.Character: StringToCharacterConverter@1143800

        – java.lang.String-> java.lang.Enum: StringToEnumConverterFactory@1bba86e

        – java.lang.String-> java.lang.Number: StringToNumberConverterFactory@18d2c12

        – java.lang.String-> java.util.Locale: StringToLocaleConverter@3598e1

        – java.lang.String-> java.util.Properties: StringToPropertiesConverter@c90828

        – java.lang.String-> java.util.UUID: StringToUUIDConverter@a42f23

        – java.util.Locale-> java.lang.String: ObjectToStringConverter@c7e20a

        – java.util.Properties-> java.lang.String: PropertiesToStringConverter@367a7f

        – java.util.UUID-> java.lang.String: ObjectToStringConverter@112b07f ......

自定义类型转换器

    ? ConversionServiceSpring 类型转换体系的核心接口。

    ? 可以利用ConversionService FactoryBeanSpring 的IOC 容器中定义一个ConversionService. Spring 将自动识别出IOC 容器中的ConversionService,并在Bean 属性配置及Spring MVC 处理方法入参绑定等场合使用它进行数据的转换

    ? 可通过ConversionServiceFactoryBeanconverters属性注册自定义的类型转换器

 
[java] view plain copy
 
 print?技术分享技术分享
  1. ——————————将字符串转换为employee对象  jsp——————————————————  
  2. <form action="testConversionServiceConverer" method="POST" >          
  3. <!-- lastname-email-gender-department.id 例如: GG-gg@atguigu.com-0-105 -->  
  4.         Employee: <input type="text" name="employee"/>  
  5.         <input type="submit" value=http://www.mamicode.com/"Submit"/>  
  6.     </form>  
  7. ————————————————action————————————————————————  
  8. @RequestMapping("/testConversionServiceConverer")  
  9.     public String testConverter(@RequestParam("employee") Employee employee){  
  10.         System.out.println("save: " + employee);  
  11.         employeeDao.save(employee);  
  12.         return "redirect:/emps";  
  13.     }  
  14. ————————————转换器————————————————————————————  
  15. @Component  
  16. public class EmployeeConverter implements Converter<String, Employee> {  
  17.   
  18.     @Override  
  19.     public Employee convert(String source) {  
  20.         if(source != null){  
  21.             String [] vals = source.split("-");  
  22.             //GG-gg@atguigu.com-0-105  
  23.             if(vals != null && vals.length == 4){  
  24.                 String lastName = vals[0];  
  25.                 String email = vals[1];  
  26.                 Integer gender = Integer.parseInt(vals[2]);  
  27.                 Department department = new Department();  
  28.                 department.setId(Integer.parseInt(vals[3]));  
  29.   
  30.                 Employee employee = new Employee(null, lastName, email, gender, department);  
  31.                 System.out.println(source + "--convert--" + employee);  
  32.                 return employee;  
  33.             }  
  34.         }  
  35.         return null;  
  36.     }  
  37. }  
  38. —————————————————springmvc.xml————————————————————————————  
  39. <!-- 配置 ConversionService -->  
  40.     <bean id="conversionService"  
  41.         class="org.springframework.format.support.FormattingConversionServiceFactoryBean">  
  42.         <property name="converters">  
  43.             <set>  
  44.                 <ref bean="employeeConverter"/>  
  45.             </set>  
  46.         </property>      
  47.     </bean>  
  48.   
  49. <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>  
 
 
 Spring 支持的转换器

 

    ? Spring 定义了3 种类型的转换器接口,实现任意一个转换器接口都可作为自定义转换器注册到ConversionServiceFactory     

中:

        – Converter<S,T>:将S 类型对象转为T 类型对象              

        – ConverterFactory:将相同系列多个“同质”Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将String转换为Number 及Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类

        – GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换

 

 

    ? <mvc:annotation-driven conversion-service=“conversionService”/>会将自定义的ConversionService注册到Spring MVC 的上下文中

        技术分享
 

 关于mvc:annotation-driven

    ? <mvc:annotation-driven/>会自动注册RequestMappingHandlerMappingRequestMappingHandlerAdapter

 与ExceptionHandlerExceptionResolver三个bean。

    ? 还将提供以下支持:

        – 支持使用ConversionService实例对表单参数进行类型转换(不加的话就没有ConversionService)

        – 支持使用@NumberFormat@DateTimeFormat注解完成数据类型的格式化

        – 支持使用@Valid注解对JavaBean 实例进行JSR 303验证

        – 支持使用@RequestBody@ResponseBody注解

 @InitBinder

    ? 由@InitBinder标识的方法,可以对WebDataBinder对象进行初始化。WebDataBinderDataBinder的子类,用于完成由表单字段到JavaBean 属性的绑定

    ? @InitBinder方法不能有返回值,它必须声明为void。

    ? @InitBinder方法的参数通常是WebDataBinder

 
[java] view plain copy
 
 print?技术分享技术分享
  1. //lastName不进行赋值,(不自动绑定对象中的lastName属性,另行处理)    user的角色赋值的时候可能会用到  
  2.     @InitBinder  
  3.     public void initBinder(WebDataBinder binder){  
  4.         binder.setDisallowedFields("lastName");  
  5.     }  
 
 数据格式化

    ? 对属性对象的输入/输出进行格式化,从其本质上讲依然属于“类型转换”的范畴。

    ? Spring 在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能

    ? FormattingConversionService拥有一个FormattingConversionServiceFactroyBean工厂类,后者用于在Spring 上下文中构造前者

    ? FormattingConversionServiceFactroyBean内部已经注册了:

        – NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用@NumberFormat注解

        – JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用@DateTimeFormat注解

    ? 装配了FormattingConversionServiceFactroyBean后,就可以在Spring MVC 入参绑定及模型数据输出时使用注解驱动了。<mvc:annotation-driven/> 默认创建的ConversionService 实例即为 FormattingConversionServiceFactroyBean

[html] view plain copy
 
 print?技术分享技术分享
  1. <mvc:annotation-driven></mvc:annotation-driven>                                                               

 日期格式化

@DateTimeFormat注解可对java.util.Date、java.util.Calendar、java.long.Long时间类型进行标注:

    – pattern 属性:类型为字符串。指定解析/格式化字段数据的模式,如:”yyyy-MM-ddhh:mm:ss”

    – iso属性:类型为DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用)-- 默认、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、ISO.DATE_TIME(yyyy-MM-ddhh:mm:ss.SSSZ)

    – style 属性:字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整日期/时间格式、-:忽略日期或时间格式

 
 数值格式化

    ? @NumberFormat可对类似数字类型的属性进行标注,它拥有两个互斥的属性:

        – style:类型为NumberFormat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类型)、Style.CURRENCY(货币类型)、Style.PERCENT(百分数类型)

        – pattern:类型为String,自定义样式,如patter="#,###";

 
 格式化示例
 
[java] view plain copy
 
 print?技术分享技术分享
  1.     @DateTimeFormat(pattern="yyyy-MM-dd")  
  2.     private Date birth;  
  3.   
  4.     @NumberFormat(pattern="#,###,###.#")  
  5.     private Float salary;  
  6.   
  7.     @RequestMapping("/handle")  
  8.     pubic String  handle(@ModelAttribute("user") User user){  
  9.     user.setId(10000);  
  10.     return "success";  
  11. }  
 
 数据校验

    ? JSR 303Java 为Bean 数据合法性校验提供的标准框架,它已经包含在JavaEE 6.0

    ? JSR 303通过Bean 属性上标注类似于@NotNull@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean 进行验证

      技术分享

 Hibernate Validator扩展注解

    ? hibernate Validator是JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解

       技术分享
      

 Spring MVC 数据校验

    ? Spring 4.0拥有自己独立的数据校验框架,同时支持JSR 303标准的校验框架。

    ? Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在Spring MVC 中,可直接通过注解驱动的方式进行数据校验

    ? Spring 的LocalValidatorFactroyBean既实现了Spring 的Validator 接口,也实现了JSR 303 的Validator接口。只要在Spring 容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean 中。

    ? Spring 本身并没有提供JSR303 的实现,所以必须将JSR303 的实现者的jar 包放到类路径下。

    ? <mvc:annotation-driven/>会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@valid注解即可让Spring MVC 在完成数据绑定后执行数据校验的工作

    ? 在已经标注了JSR303 注解的表单/命令对象前标注一个@ValidSpring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验

    ? Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是BindingResultErrors类型,这两个类都位于org.springframework.validation包中

    ? 需校验的Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参

    ? Errors 接口提供了获取错误信息的方法,如getErrorCount() 或getFieldErrors(String field)

    ?  BindingResult扩展了Errors 接口

                技术分享

 在目标方法中获取校验结果

    ? 在表单/命令对象类的属性中标注校验注解,在处理方法对应的入参前添加@Valid,Spring MVC 就会实施校验并将校验结果保存在被校验入参对象之后的BindingResult或Errors 入参中。

? 常用方法:

    – FieldErrorgetFieldError(String field)

    – List<FieldError> getFieldErrors()

    – Object getFieldValue(String field)

    – Int getErrorCount()

 
 

 在页面上显示错误

    ? Spring MVC 除了会将表单/命令对象的校验结果保存到对应的BindingResult或Errors 对象中外,还会将所有校验结果保存到“隐含模型

    ? 即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在“隐含对象”中。

    ? 隐含模型中的所有数据最终将通过HttpServletRequest的属性列表暴露给JSP 视图对象,因此在JSP 中可以获取错误信息

    ? 在JSP 页面上可通过<form:errorspath=“userName”> 显示错误消息

 

  提示消息的国际化

    ? 每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的FieldError对象。

    ? 当一个属性校验失败后,校验框架会为该属性生成4 个消息代码,这些代码以校验注解类名为前缀,结合modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如User 类中的password 属性标准了一个@Pattern 注解,当该属性值不满足@Pattern 所定义的规则时, 就会产生以下4 个错误代码:

       –  Pattern.user.password

        – Pattern.password

        – Pattern.java.lang.String

        – Pattern

    ? 当使用Spring MVC 标签显示错误消息时,Spring MVC 会查看WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。

 

 

    ? 若数据类型转换或数据格式转换时发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:

        – required:必要的参数不存在。如@RequiredParam(“param1”) 标注了一个入参,但是该参数不存在

        – typeMismatch:在数据绑定时,发生数据类型不匹配的问题

        – methodInvocationSpring MVC 在调用处理方法时发生了错误–注册国际化资源文件

Spring MVC 学习笔记(二)