手记

SpringMVC数据类型转换器与国际化配置

SpringMVC数据类型转换器

我们都知道在浏览器中,访问网页都是通过url访问的。所以在web开发中,我们经常会通过url来传递数据。如果我要在url参数上传递一个日期数据,我们是无法在后端通过声明一个Date类型的参数来接收的,会报空指针错误。想要接收日期数据,就得用到SpringMVC中的数据类型转换器了。

在SpringMVC转换中有三种方式可以转换数据类型:

1.在控制中加入一个方法,在该方法上写上@InitBinder注解,并且在方法参数上声明一个WebDataBinder类型参数。这个方法会在控制器中其他方法之前调用,所以在该方法中就可以预先处理数据类型的转换。这里我们需要使用一个实现了PropertyEditor接口或者继承了PropertyEditorSupport类的自定义类型转换器来进行类型的转换。如下示例:

package org.zero01.test;import org.springframework.beans.propertyeditors.CustomDateEditor;import org.springframework.stereotype.Controller;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.InitBinder;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.text.SimpleDateFormat;import java.util.Date;@Controllerpublic class TestController {    @RequestMapping("test.do")    @ResponseBody    public String test(Date date) {        System.out.println("我被调用了-test");        return date.toString();    }    @InitBinder    public void initDate(WebDataBinder webDataBinder) {        System.out.println("我被调用了-initDate");        SimpleDateFormat sig = new SimpleDateFormat("yyy-MM-dd");        // 第一个参数是DataDateFormat类型的对象,第二个参数指定是否允许为空        CustomDateEditor cue = new CustomDateEditor(sig, true);        // 注册自定义的日期转换格式        webDataBinder.registerCustomEditor(Date.class, cue);    }}

浏览器访问http://localhost:8080/test.do?date=2018-01-01,输出结果如下:

Mon Jan 01 00:00:00 CST 2018

控制台打印结果如下:

我被调用了-initDate我被调用了-test

以上这种转换数据类型的方式只是局部的,也就是说只能在一个控制器中使用,如果希望是全局有效的话,我们就需要在Spring配置文件中注册一个转换器了。但是在这之前我们需要先自定义一个类并实现一个Formatter接口,如下示例:

package org.zero01.test;import org.springframework.format.Formatter;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Locale;// 接口的泛型用于指定转换成什么类型public class MyDataFormatter implements Formatter<Date> {    // 在parse定义转换的格式    public Date parse(String s, Locale locale) throws ParseException {        SimpleDateFormat sig = new SimpleDateFormat("yyyy-MM-dd");        return sig.parse(s);    }    public String print(Date date, Locale locale) {        return null;    }}

在Spring配置文件中注册formatters转换器:

<mvc:annotation-driven conversion-service="myDateFormatters"/><bean id="myDateFormatters" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">    <property name="formatters">        <set>            <bean class="org.zero01.test.MyDataFormatter" />        </set>    </property></bean>

控制器代码如下:

package org.zero01.test;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.Date;@Controllerpublic class TestController {    @RequestMapping("test.do")    @ResponseBody    public String test(Date date) {        return date.toString();    }}

浏览器访问http://localhost:8080/test.do?date=2018-03-28,输出结果如下:

Mon Jan 02 00:00:00 CST 2018

通过实现Formatter接口来实现类型的转换有一个缺点就是无法自定义来源类型,Formatter接口默认的来源类型都是String,而目标类型则可以自定义。如果希望能够自定义来源类型的话,就需要实现Converter接口,通过该接口我们可以指定来源类型以及转换后的目标类型。如下示例:

package org.zero01.test;import org.springframework.core.convert.converter.Converter;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;public class MyDateConvert implements Converter<String, Date> {    public Date convert(String s) {        SimpleDateFormat sig = new SimpleDateFormat("yyyy-MM-dd");        try {            return sig.parse(s);        } catch (ParseException e) {            e.printStackTrace();        }        return null;    }}

同样的需要注册转换器,在Spring配置文件中注册converters转换器:

<mvc:annotation-driven conversion-service="myDateConvert"/><bean id="myDateConvert" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">    <property name="converters">        <set>            <bean class="org.zero01.test.MyDateConvert" />        </set>    </property></bean>

控制器代码和之前一样,略。浏览器访问http://localhost:8080/test.do?date=2018-03-28,输出结果如下:

Mon Jan 02 00:00:00 CST 2018

HttpMessageConverter接口

说到转换器,这里不得不再介绍一个HttpMessageConverter,这是Spring3.x中引入的接口,在底层中它作为一个消息转换器存在。SpringMVC使用消息转换器(HttpMessageConverter)实现将请求信息转换为对象、将对象转换为响应信息。

我们在使用SpringMVC时经常会使用到@RequestBody和@ResponseBody两个注解,例如上面的代码就用到了@ResponseBody注解。它们分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是HttpMessageConverter的应用,通过不同的HttpMessageConverter实现类就可以进行不同类型的转换。

默认情况下@ResponseBody注解会把返回的数据转换成普通的文本数据进行处理,而我们如果配置了JSON的转换器的话,就会按照JSON格式进行转换。这也是抽象了HttpMessageConverter接口的好处,可以在不同类型的数据间进行转换。

HttpMessageConverter消息转换器最高层次的接口抽象,描述了一个消息转换器的一般特征,我们可以来看一下HttpMessageConverter接口的源码:

public interface HttpMessageConverter<T> {      //判断数据类型是否可读      boolean canRead(Class<?> clazz, MediaType mediaType);      //判断数据是否可写      boolean canWrite(Class<?> clazz, MediaType mediaType);      //获取支持的数据类型      List<MediaType> getSupportedMediaTypes();      //对参数值进行读,转换为需要的类型      T read(Class<? extends T> clazz, HttpInputMessage inputMessage)              throws IOException, HttpMessageNotReadableException;      //将返回值发送给请求者      void write(T var1, MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;}

如果我们的控制器中有这样一个方法:

@RequestMapping(value="/string", method=RequestMethod.POST)public @ResponseBody String readString(@RequestBody String string) {    return "Read string '" + string + "'";}

在SpringMVC进入readString方法前,会根据@RequestBody注解选择适当的HttpMessageConverter实现类来将请求参数解析到string变量中,具体来说是使用了StringHttpMessageConverter类,它的canRead()方法返回true,然后它的read()方法会从请求中读出请求参数,绑定到readString()方法的string变量中。

当SpringMVC执行readString方法后,由于返回值标识了@ResponseBody,SpringMVC将使用StringHttpMessageConverter的write()方法,将结果作为String值写入响应报文,当然,此时canWrite()方法返回true。

我们可以用下面的图,简单描述一下这个过程:


springMVC国际化配置和使用

有些时候我们可能会有不同语言之间切换的需求,通过SpringMVC国际化配置,可以实现简单的语言切换,下面使用一个小demo演示一下如何进行国际化的配置。

web.xml文件配置如下:

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"         version="3.1">    <welcome-file-list>        <welcome-file>/WEB-INF/index.jsp</welcome-file>    </welcome-file-list>    <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>        <init-param>            <param-name>forceEncoding</param-name>            <param-value>true</param-value>        </init-param>    </filter>    <filter-mapping>        <filter-name>CharacterEncodingFilter</filter-name>        <url-pattern>/</url-pattern>    </filter-mapping>    <servlet>        <servlet-name>DispatcherServlet</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value>/WEB-INF/applicationContext*.xml,classpath*:applicationContext*.xml</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>DispatcherServlet</servlet-name>        <url-pattern>*.do</url-pattern>    </servlet-mapping>    <listener>        <listener-class>            org.springframework.web.context.ContextLoaderListener        </listener-class>    </listener></web-app>

在Spring配置文件中加入以下内容:

<!-- 默认国际化语言配置  --><bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">    <property name="defaultLocale" value="zh_CN"/></bean><!-- 区域解析类 自动解析 --><bean id="ahr" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"/><bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">    <property name="basename" value="/WEB-INF/i18n/message"/></bean>

然后创建不同语言的资源文件:

文件内容:

message_en_US.properties文件内容如下:username=UserNamepassword=Passwordmessage_zh_CN.properties文件内容如下:username=\u7528\u6237\u540dpassword=\u5bc6\u7801message_zh_TW.properties文件内容如下:username=\u7528\u6236\u540dpassword=\u5bc6\u78bc

编写控制器代码如下:

package org.zero01.test;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.i18n.SessionLocaleResolver;import javax.servlet.http.HttpSession;import java.util.Locale;@Controllerpublic class TestController {    @RequestMapping("test.do")    public String testLocation(String lang, HttpSession session) {        Locale locale = null;        if (lang.equals("en") || lang.equals("us")) {            locale = new Locale("en", "US");        } else if (lang.equals("tw")) {            locale = new Locale("zh", "TW");        } else {            locale = new Locale("zh", "CN");        }        session.setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale);        return "index";    }}

index.jsp文件内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %><html><head>    <title>Title</title></head><style>    .link{        color: #888;        text-decoration: none;    }    .link:hover{        color: #e60023;    }</style><body>    <div>        <spring:message code="username"/> |        <spring:message code="password"/><br>        [<a href="test.do?lang=us" class="link"> 英文</a>]        [<a href="test.do?lang=zh" class="link">中文</a>]        [<a href="test.do?lang=tw" class="link">繁体</a>]    </div></body></html>


0人推荐
随时随地看视频
慕课网APP