ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

12.Spring MVC类型转换器(Converter)

2022-07-31 14:33:43  阅读:137  来源: 互联网

标签:12 Converter Spring springframework 转换器 类型 org String


我想您一定十分诧异,为什么仅仅通过一些注解,控制器方法就能够得到各种类型的参数,其实这都要归功于 Spring MVC 的类型转换机制。

Spring 提供了一种 Converter(类型转换器)的类型转换工具。在 Spring MVC 中,它的作用是在控制器方法对请求进行处理前,先获取到请求发送过来的参数,并将其转换为控制器方法指定的数据类型,然后再将转换后的参数值传递给控制器方法的形参,这样后台的控制器方法就可以正确地获取请求中携带的参数了。

内置的类型转换器

Spring MVC 框架默认提供了许多内置的类型转换器,主要包括以下几种类型。

1)标量转换器

名称 作用
StringToBooleanConverter String 到 boolean 类型转换
ObjectToStringConverter Object 到 String 转换,调用 toString 方法转换
StringToNumberConverterFactory String 到数字转换(例如 Integer、Long 等)
NumberToNumberConverterFactory 数字子类型(基本类型)到数字类型(包装类型)转换
StringToCharacterConverter String 到 Character 转换,取字符串中的第一个字符
NumberToCharacterConverter 数字子类型到 Character 转换
CharacterToNumberFactory Character 到数字子类型转换
StringToEnumConverterFactory String 到枚举类型转换,通过 Enum.valueOf 将字符串转换为需要的枚举类型
EnumToStringConverter 枚举类型到 String 转换,返回枚举对象的 name 值
StringToLocaleConverter String 到 java.util.Locale 转换
PropertiesToStringConverter java.util.Properties 到 String 转换,默认通过 ISO-8859-1 解码
StringToPropertiesConverter String 到 java.util.Properties 转换,默认使用 ISO-8859-1 编码

2)集合、数组相关转换器

名称 作用
ArrayToCollectionConverter 任意数组到任意集合(List、Set)转换
CollectionToArrayConverter 任意集合到任意数组转换
ArrayToArrayConverter 任意数组到任意数组转换
CollectionToCollectionConverter 集合之间的类型转换
MapToMapConverter Map之间的类型转换
ArrayToStringConverter 任意数组到 String 转换
StringToArrayConverter 字符串到数组的转换,默认通过“,”分割,且去除字符串两边的空格(trim)
ArrayToObjectConverter 任意数组到 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回数组的第一个元素并进行类型转换
ObjectToArrayConverter Object 到单元素数组转换
CollectionToStringConverter 任意集合(List、Set)到 String 转换
StringToCollectionConverter String 到集合(List、Set)转换,默认通过“,”分割,且去除字符串两边的空格(trim)
CollectionToObjectConverter 任意集合到任意 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回集合的第一个元素并进行类型转换
ObjectToCollectionConverter Object 到单元素集合的类型转换


Spring MVC 对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换。因此,通常情况下 Spring MVC 提供的这些类型转换器可以满足开发人员大多数的类型转换需求的。

注意:在使用内置类型转换器时,请求参数输入值需要与接收参数类型相兼容,否则会报 400 错误。

自定义类型转换器

一般情况下,Spring MVC 内置的类型转换器就可以满足我们日常的开发需求,但对于一些较为复杂类型的转换,例如 String 转换 Date 类型,以及开发人员自定义格式的数据的转换等,就需要我们根据自身的需求开发自定义类型转换器来转换了。

1. 创建自定义类型转换器类

Spring 在 org.springframework.core.convert.converter 包中定义了 3 种类型的转换器接口,如下表。

接口 说明
Converter<S,T> 该接口使用了泛型,第一个类型 S 表示原类型,第二个类型 T 表示目标类型,里面定义了一个 convert() 方法,能够将原类型对象作为参数传入,进行转换之后返回目标类型对象。
ConverterFactory 如果我们希望将一种类型的对象转换为另一种类型及其子类对象,例如将 String 转换为 Number 以及 Number 的子类 Integer、Double 等类型的对象,那么就需要一系列的 Converter,如 StringToInteger、StringToDouble 等。ConverterFactory<S,R> 接口的作用就是将这些相同系列的多个 Converter 封装在一起。
GenericConverter 该接口会根据源类对象及目标类对象的上下文信息进行类型转换。


如果我们想要自定义类型转换器,第一步就是要创建一个自定义类型转换器类,并实现以上 3 个接口的中任意一种转换器接口即可。

例如,下面的代码就是一个实现了 Converter<S,T> 接口的自定义类型转换器, 该类型转换器可以将 String 类型转换为 Date 类型,代码如下。

  1. package net.biancheng.c.converter;
  2.  
  3. import org.springframework.core.convert.converter.Converter;
  4.  
  5. import java.text.ParseException;
  6. import java.text.SimpleDateFormat;
  7. import java.util.Date;
  8.  
  9. /**
  10. * 自定义日期转换器
  11. */
  12. public class MyDateConverter implements Converter<String, Date> {
  13. private String datePatten = "yyyy-MM-dd";
  14.  
  15. @Override
  16. public Date convert(String source) {
  17. System.out.println("前端页面传递过来的时间为:" + source);
  18. SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePatten);
  19. try {
  20. return simpleDateFormat.parse(source);
  21. } catch (ParseException e) {
  22. throw new IllegalArgumentException("无效的日期格式,请使用正确的日期格式" + datePatten);
  23. }
  24. }
  25. }

2. 配置自定义类型转换器

在创建完自定义类型转换器后,我们还需要在 Spring MVC 的核心配置文件中对它进行配置,这个自定义类型装换器才会生效,示例配置如下。

  1. <!--显式地装配自定义类型转换器-->
  2. <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
  3.  
  4. <!--自定义类型转换器配置-->
  5. <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
  6. <property name="converters">
  7. <set>
  8. <bean class="net.biancheng.c.converter.MyDateConverter"></bean>
  9. </set>
  10. </property>
  11. </bean>


在上面的配置中,我们共完成以下 2 步配置:

  1. 通过 Spring MVC 的配置文件,在 Spring 容器中声明一个 org.springframework.context.support.ConversionServiceFactoryBean 的 Bean(例如示例配置中的名为 conversionService 的 Bean),然后通过其 converters 属性将自定义的类型转换器注册到其中。
  2. 通过 <mvc:annotation-driven> 标签的 conversion-service 属性,将我们声明的 ConversionServiceFactoryBean 类型的 Bean 显式地将其默认注册的 ConversionService(FormattingConversionServiceFactoryBean 类型)覆盖掉。

通常情况下,我们都需要在 Spring MVC 的核心配置文件中配置一个 <mvc:annotation-driven>,它是 Spring MVC 提供的注解驱动标签,使用该标签能够简化 Spring MVC 的相关配置。

<mvc:annotation-driven> 会自动向 Spring MVC 中注册 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 以及 ExceptionHandlerExceptionResolver 三个 Bean。其中,RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 都是控制器方法对请求进行分发的必须组件,而 ExceptionHandlerExceptionResolver 则是 Spring MVC 的异常处理组件。

除此之外,<mvc:annotation-driven> 标签还默认注册了一个 org.springframework.format.support.FormattingConversionServiceFactoryBean 类型的 Bean:ConversionService。通过它,可以满足我们大多数的类型转换需求。

示例

我们以 Converter<S,T> 为例,来演示下如何通过实现该接口来自定义类型转换器。

1 新建一个名为 springmvc-converter-demo 的 Web 项目,并将与 Sprng MVC 相关的依赖引入到该工程中,其 web.xml 的配置如下。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6.  
  7. <!--请求和响应的字符串过滤器-->
  8. <filter>
  9. <filter-name>CharacterEncodingFilter</filter-name>
  10. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  11. <init-param>
  12. <param-name>encoding</param-name>
  13. <param-value>UTF-8</param-value>
  14. </init-param>
  15. <init-param>
  16. <param-name>forceResponseEncoding</param-name>
  17. <param-value>true</param-value>
  18. </init-param>
  19. </filter>
  20. <filter-mapping>
  21. <filter-name>CharacterEncodingFilter</filter-name>
  22. <url-pattern>/*</url-pattern>
  23. </filter-mapping>
  24.  
  25. <!--来处理 PUT 和 DELETE 请求的过滤器-->
  26. <filter>
  27. <filter-name>HiddenHttpMethodFilter</filter-name>
  28. <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  29. </filter>
  30. <filter-mapping>
  31. <filter-name>HiddenHttpMethodFilter</filter-name>
  32. <url-pattern>/*</url-pattern>
  33. </filter-mapping>
  34.  
  35. <!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
  36. <servlet>
  37. <servlet-name>dispatcherServlet</servlet-name>
  38. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  39. <!--配置 DispatcherServlet 的一个初始化参数:spring mvc 配置文件按的位置和名称-->
  40. <init-param>
  41. <param-name>contextConfigLocation</param-name>
  42. <param-value>classpath:springMVC.xml</param-value>
  43. </init-param>
  44.  
  45. <!--作为框架的核心组件,在启动过程中有大量的初始化操作要做
  46. 而这些操作放在第一次请求时才执行会严重影响访问速度
  47. 因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时-->
  48. <load-on-startup>1</load-on-startup>
  49. </servlet>
  50.  
  51. <servlet-mapping>
  52. <servlet-name>dispatcherServlet</servlet-name>
  53. <!--设置springMVC的核心控制器所能处理的请求的请求路径
  54. /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
  55. 但是/不能匹配.jsp请求路径的请求-->
  56. <url-pattern>/</url-pattern>
  57. </servlet-mapping>
  58.  
  59. </web-app>

 
2. 在 src(类路径下)创建一个 Spring MVC 的核心配置文件 springMVC.xml,配置内容如下。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
  10.  
  11. <!--开启组件扫描-->
  12. <context:component-scan base-package="net.biancheng.c"></context:component-scan>
  13.  
  14. <!-- 配置 Thymeleaf 视图解析器 -->
  15. <bean id="viewResolver"
  16. class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
  17. <property name="order" value="1"/>
  18. <property name="characterEncoding" value="UTF-8"/>
  19. <property name="templateEngine">
  20. <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
  21. <property name="templateResolver">
  22. <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
  23. <!-- 视图前缀 -->
  24. <property name="prefix" value="/WEB-INF/templates/"/>
  25. <!-- 视图后缀 -->
  26. <property name="suffix" value=".html"/>
  27. <property name="templateMode" value="HTML5"/>
  28. <property name="characterEncoding" value="UTF-8"/>
  29. </bean>
  30. </property>
  31. </bean>
  32. </property>
  33. </bean>
  34.  
  35. <!-- view-name:设置请求地址所对应的视图名称-->
  36. <mvc:view-controller path="/" view-name="user"></mvc:view-controller>
  37.  
  38. <!--当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:-->
  39. <mvc:annotation-driven></mvc:annotation-driven>
  40.  
  41. </beans>


3. 在 net.biancheng.c.bean 包下,创建一个名为 User 的实体类,代码如下。

  1. package net.biancheng.c.bean;
  2.  
  3. import java.util.Date;
  4.  
  5. /**
  6. * 实体类 User
  7. */
  8. public class User {
  9.  
  10. private String userName;
  11. private Date birth;
  12. private Double height;
  13.  
  14. public String getUserName() {
  15. return userName;
  16. }
  17.  
  18. public void setUserName(String userName) {
  19. this.userName = userName;
  20. }
  21.  
  22. public Date getBirth() {
  23. return birth;
  24. }
  25.  
  26. public void setBirth(Date birth) {
  27. this.birth = birth;
  28. }
  29.  
  30. public Double getHeight() {
  31. return height;
  32. }
  33.  
  34. public void setHeight(Double height) {
  35. this.height = height;
  36. }
  37.  
  38. @Override
  39. public String toString() {
  40. return "User{" +
  41. "userName='" + userName + '\'' +
  42. ", height=" + height +
  43. ", birth=" + birth +
  44. '}';
  45. }
  46. }


注:从 User 的代码中,我们可以看出 User 共包含 userName(姓名)、birth(生日)和 height(身高)三个属性,其中 userName 为 String 类型,birth 为 Date 类型,height 为 Double 类型。

4. 在 net.biancheng.c.controller 包下,创建一个名为 UserController 的 Controller 类,代码如下。

  1. package net.biancheng.c.controller;
  2.  
  3. import net.biancheng.c.bean.User;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.ui.Model;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RequestMethod;
  8.  
  9. /**
  10. * @author C语言中文网
  11. */
  12. @Controller
  13. public class UserController {
  14.  
  15. @RequestMapping(value = "/user", method = RequestMethod.POST)
  16. public String login(User user, Model model) {
  17. System.out.println(user);
  18. model.addAttribute("user", user);
  19. return "success";
  20. }
  21.  
  22. }


5. 在 webapp/WEB-INF/ 下新建一个 templates 目录,并在该目录中创建一个 user.html,代码如下。

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>C语言中文网</title>
  6. </head>
  7. <body>
  8. <form th:action="@{/user}" method="post">
  9. <table>
  10. <tr>
  11. <td>姓名:</td>
  12. <td><input type="text" name="userName" required><br></td>
  13. </tr>
  14.  
  15. <tr>
  16. <td>生日:</td>
  17. <td><input type="date" name="birth" required><br></td>
  18. </tr>
  19. <tr>
  20. <td>身高:</td>
  21. <td><input type="text" name="height" required><br></td>
  22. </tr>
  23. <tr>
  24. <td colspan="2" align="center">
  25. <input type="submit" value="提交">
  26. <input type="reset" value="重置">
  27. </td>
  28.  
  29. </tr>
  30. </table>
  31. </form>
  32. </body>
  33. </html>


6. 在 webapp/WEB-INF/templates 目录下创建一个 success.html,代码如下。

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>C语言中文网</title>
  6. </head>
  7. <body>
  8. <table>
  9. <tr>
  10. <td>用户名:</td>
  11. <td th:text="${user.getUserName()}"></td>
  12. </tr>
  13. <tr>
  14. <td>生日:</td>
  15. <td th:text="${#dates.format(user.getBirth(),'yyyy-MM-dd')}"></td>
  16. </tr>
  17. <tr>
  18. <td>身高:</td>
  19. <td th:text="${user.getHeight()}"></td>
  20. </tr>
  21. </table>
  22. </body>
  23. </html>


7. 将 springmvc-converter-demo 部署到 Tomcat 服务器中,启动 Tomcat,使用浏览器“http://localhost:8080/springmvc-converter-demo/”,结果如下图。


图1:用户登记页面

8. 点击“提交”按钮,结果出现错误,如下图。


图2:错误页

9. 查看控制台,错误日志如下图。

14:09:48.585 [http-nio-8080-exec-2] WARN org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<LF>Field error in object 'user' on field 'birth': rejected value [2000-06-21]; codes [typeMismatch.user.birth,typeMismatch.birth,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.birth,birth]; arguments []; default message [birth]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'birth'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2000-06-21'; nested exception is java.lang.IllegalArgumentException]]
14:09:48.587 [http-nio-8080-exec-2] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 400 BAD_REQUEST


注:从控制台输出的日志可以看出,出现该错误的原因是字符串“2000-06-21”从 String 类型转换到 Date 类型时失败。

10. 新建 net.biancheng.c.converter 包,并在该包中创建名一个名为 MyDateConverter 的自定义类型转换器类,代码如下。

  1. package net.biancheng.c.converter;
  2.  
  3. import org.springframework.core.convert.converter.Converter;
  4.  
  5. import java.text.ParseException;
  6. import java.text.SimpleDateFormat;
  7. import java.util.Date;
  8.  
  9. /**
  10. * 自定义日期转换器
  11. */
  12. public class MyDateConverter implements Converter<String, Date> {
  13. private String datePatten = "yyyy-MM-dd";
  14.  
  15. @Override
  16. public Date convert(String source) {
  17. System.out.println("前端页面传递过来的时间为:" + source);
  18. SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePatten);
  19. try {
  20. return simpleDateFormat.parse(source);
  21. } catch (ParseException e) {
  22. throw new IllegalArgumentException("无效的日期格式,请使用正确的日期格式" + datePatten);
  23. }
  24. }
  25. }


11. 修改 springMVC.xml 的配置,将我们自定义的类型转换器注册到 Spring 容器中。

  1. <!--显式地装配自定义类型转换器-->
  2. <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
  3.  
  4. <!--自定义类型转换器配置-->
  5. <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
  6. <property name="converters">
  7. <set>
  8. <bean class="net.biancheng.c.converter.MyDateConverter"></bean>
  9. </set>
  10. </property>
  11. </bean>

 

12. 重启 Tomcat 服务器,重新执行第 7-8 步,结果如下图。


图3:成功页

 

标签:12,Converter,Spring,springframework,转换器,类型,org,String
来源: https://www.cnblogs.com/55zjc/p/16537081.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有