SpringMVC+FastJson 自定义日期转换器

Tim7000 4年前

来自: http://my.oschina.net/pwh19920920/blog/614066


对于有的时候要输出日期格式为yyyy-MM-dd,而有的时候要输出yyyy-MM-dd hh:mm:ss时怎么办?

第一种方案:纯注解式, 对日期类型的字段进行注解

@JSONField(format = "yyyy-MM-dd")  private Date updateDate;    @JSONField(format = "yyyy-MM-dd hh:mm:ss")  private Date createDate;    public Date getUpdateDate() {      return updateDate;  }    public void setUpdateDate(Date updateDate) {      this.updateDate = updateDate;  }    public void setCreateDate(Date createDate) {      this.createDate = createDate;  }    public Date getCreateDate() {      return createDate;  }


第二种方案:使用fastjson的<value>WriteDateUseDateFormat</value>配置(使得返回的日期类型默认为yyyy-MM-dd hh:mm:ss), 特殊类型使用字段@JSONField来进行控制

<!-- 默认的注解映射的支持,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping -->  <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">     <mvc:message-converters register-defaults="true">        <!-- 将Jackson2HttpMessageConverter的默认格式化输出为true -->        <!-- 配置Fastjson支持 -->        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">           <property name="supportedMediaTypes">              <list>                 <value>text/html;charset=UTF-8</value>                 <value>application/json</value>              </list>           </property>           <property name="features">              <list>                 <!-- 输出key时是否使用双引号 -->                 <value>QuoteFieldNames</value>                 <!-- 是否输出值为null的字段 -->                 <!-- <value>WriteMapNullValue</value> -->                 <!-- 数值字段如果为null,输出为0,而非null -->                 <value>WriteNullNumberAsZero</value>                 <!-- List字段如果为null,输出为[],而非null -->                 <value>WriteNullListAsEmpty</value>                 <!-- 字符类型字段如果为null,输出为"",而非null -->                 <value>WriteNullStringAsEmpty</value>                 <!-- Boolean字段如果为null,输出为false,而非null -->                 <value>WriteNullBooleanAsFalse</value>                 <!-- null String不输出  -->                 <value>WriteNullStringAsEmpty</value>                 <!-- null String也要输出  -->                 <!-- <value>WriteMapNullValue</value> -->                                  <!-- Date的日期转换器 -->                 <value>WriteDateUseDateFormat</value>              </list>           </property>        </bean>     </mvc:message-converters>  </mvc:annotation-driven>    <!-- REST中根据URL后缀自动判定Content-Type及相应的View -->  <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">     <property name="mediaTypes" >        <map>           <entry key="json" value="application/json"/>        </map>     </property>     <!-- 这里是否忽略掉accept header,默认就是false -->     <property name="ignoreAcceptHeader" value="true"/>     <property name="favorPathExtension" value="true"/>  </bean>
@JSONField(format = "yyyy-MM-dd")  private Date updateDate;    public Date getUpdateDate() {      return updateDate;  }    public void setUpdateDate(Date updateDate) {      this.updateDate = updateDate;  }


第三种方案:使用FastJson的消息转换器, 特殊类型使用字段@JSONField来进行控制

import com.alibaba.fastjson.JSON;  import com.alibaba.fastjson.serializer.SerializeConfig;  import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;  import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;  import org.springframework.http.HttpOutputMessage;  import org.springframework.http.converter.HttpMessageNotWritableException;    import java.io.IOException;  import java.io.OutputStream;    /**   * 如果没有注入默认的日期格式,也没有配置<value>WriteDateUseDateFormat</value>, 也没有属性注解@JSONField(format="yyyy-MM-dd hh:mm:ss") 则会转换输出时间戳   * 如果只配置<value>WriteDateUseDateFormat</value>,则会转换输出yyyy-MM-dd hh:mm:ss   * 配置<value>WriteDateUseDateFormat</value>, 属性注解@JSONField(format="yyyy-MM-dd hh:mm:ss") 则会转换输出为属性注解的格式   * 如果注入了默认的日期格式,属性注解@JSONField(format="yyyy-MM-dd hh:mm:ss") 则会转换输出为属性注解的格式   * 如果注入了默认的日期格式,则会转换输出为默认的日期格式   * 如果三者都配置则会转换成属性注解的格式   * Created by PETER on 2016/2/5.   */  public class CustomerFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter {        public static SerializeConfig mapping = new SerializeConfig();        private String defaultDateFormat;        @Override      protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {          OutputStream out = outputMessage.getBody();          String text = JSON.toJSONString(obj, mapping, super.getFeatures());          byte[] bytes = text.getBytes(getCharset());          out.write(bytes);      }        public void setDefaultDateFormat(String defaultDateFormat) {          mapping.put(java.util.Date.class, new SimpleDateFormatSerializer(defaultDateFormat));      }  }
<?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        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/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">       <description>Spring MVC Configuration</description>       <!-- 加载配置属性文件 -->     <context:property-placeholder ignore-unresolvable="true" location="classpath:/xmutca.properties" />       <!-- 扫描dubbo注解需要在controller之前,否则会造成无法注入的问题 -->     <dubbo:annotation package="com.xmutca"></dubbo:annotation>       <!-- 使用Annotation自动注册Bean,只扫描@Controller -->     <context:component-scan base-package="com.xmutca" use-default-filters="false">        <!-- base-package 如果多个,用“,”分隔 -->        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>     </context:component-scan>       <!-- 默认的注解映射的支持,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping -->     <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">        <mvc:message-converters register-defaults="true">           <!-- 将Jackson2HttpMessageConverter的默认格式化输出为true -->           <!-- 配置Fastjson支持 -->           <bean class="com.ydyx.core.web.converter.CustomerFastJsonHttpMessageConverter">              <property name="supportedMediaTypes">                 <list>                    <value>text/html;charset=UTF-8</value>                    <value>application/json</value>                 </list>              </property>              <property name="features">                 <list>                    <!-- 输出key时是否使用双引号 -->                    <value>QuoteFieldNames</value>                    <!-- 是否输出值为null的字段 -->                    <!-- <value>WriteMapNullValue</value> -->                    <!-- 数值字段如果为null,输出为0,而非null -->                    <value>WriteNullNumberAsZero</value>                    <!-- List字段如果为null,输出为[],而非null -->                    <value>WriteNullListAsEmpty</value>                    <!-- 字符类型字段如果为null,输出为"",而非null -->                    <value>WriteNullStringAsEmpty</value>                    <!-- Boolean字段如果为null,输出为false,而非null -->                    <value>WriteNullBooleanAsFalse</value>                    <!-- null String不输出  -->                    <value>WriteNullStringAsEmpty</value>                    <!-- null String也要输出  -->                    <!-- <value>WriteMapNullValue</value> -->                 </list>              </property>              <property name="defaultDateFormat" value="yyyy-MM-dd"></property>           </bean>        </mvc:message-converters>     </mvc:annotation-driven>       <!-- REST中根据URL后缀自动判定Content-Type及相应的View -->     <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">        <property name="mediaTypes" >           <map>              <entry key="json" value="application/json"/>           </map>        </property>        <!-- 这里是否忽略掉accept header,默认就是false -->        <property name="ignoreAcceptHeader" value="true"/>        <property name="favorPathExtension" value="true"/>     </bean>       <!-- 视图文件解析配置 -->     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">        <property name="prefix" value="${web.view.prefix}"/>        <property name="suffix" value="${web.view.suffix}"/>     </bean>       <!-- 对静态资源文件的访问, 将无法mapping到Controller的path交给default servlet handler处理 -->     <mvc:default-servlet-handler/>       <!-- 定义无Controller的path<->view直接映射 -->     <mvc:view-controller path="/" view-name="redirect:${web.view.index}"/>       <!-- 基于注解式子的异常处理 -->     <bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean>     <!-- Shiro end -->       <!-- 上传文件拦截,设置最大上传文件大小   10M=10*1024*1024(B)=10485760 bytes -->     <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">        <property name="maxUploadSize" value="${web.maxUploadSize}" />     </bean>  </beans>


第四种方案:使用SpringMVC的自定义属性编辑器

@InitBinder  protected void initBinder(WebDataBinder binder) {      // String类型转换,将所有传递进来的String进行前后空格处理, null字符串处理      binder.registerCustomEditor(String.class, new PropertyEditorSupport() {          @Override          public void setAsText(String text) {              setValue(text == null ? null : text.trim());          }            @Override          public String getAsText() {              Object value = getValue();              return value != null ? value.toString() : "";          }      });        // Date 类型转换      binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {          @Override          public void setAsText(String text) {              setValue(DateUtils.parseDate(text));          }            @Override          public String getAsText() {              Date date = (Date) getValue();              return DateUtils.formatDate(date, "yyyy-MM-dd");          }      });  }


DateUtils源代码:

import java.text.ParseException;  import java.text.SimpleDateFormat;  import java.util.Date;    import org.apache.commons.lang.time.DateFormatUtils;    /**   * 日期工具类, 继承org.apache.commons.lang.time.DateUtils类   *    */  public class DateUtils extends org.apache.commons.lang3.time.DateUtils {       private static String[] parsePatterns = { "yyyy-MM-dd",           "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",           "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm" ,"yyyyMMdd"};       /**      * 得到当前日期字符串 格式(yyyy-MM-dd)      */     public static String getDate() {        return getDate("yyyy-MM-dd");     }       /**      * 得到当前日期字符串 格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"      */     public static String getDate(String pattern) {        return DateFormatUtils.format(new Date(), pattern);     }       /**      * 得到日期字符串 默认格式(yyyy-MM-dd) pattern可以为:"yyyy-MM-dd" "HH:mm:ss" "E"      */     public static String formatDate(Date date, Object... pattern) {        String formatDate = null;        if (pattern != null && pattern.length > 0) {           formatDate = DateFormatUtils.format(date, pattern[0].toString());        } else {           formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");        }        return formatDate;     }       /**      * 得到日期时间字符串,转换格式(yyyy-MM-dd HH:mm:ss)      */     public static String formatDateTime(Date date) {        return formatDate(date, "yyyy-MM-dd HH:mm:ss");     }       /**      * 得到当前时间字符串 格式(HH:mm:ss)      */     public static String getTime() {        return formatDate(new Date(), "HH:mm:ss");     }       /**      * 得到当前日期和时间字符串 格式(yyyy-MM-dd HH:mm:ss)      */     public static String getDateTime() {        return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");     }       /**      * 得到当前年份字符串 格式(yyyy)      */     public static String getYear() {        return formatDate(new Date(), "yyyy");     }       /**      * 得到当前月份字符串 格式(MM)      */     public static String getMonth() {        return formatDate(new Date(), "MM");     }       /**      * 得到当天字符串 格式(dd)      */     public static String getDay() {        return formatDate(new Date(), "dd");     }       /**      * 得到当前星期字符串 格式(E)星期几      */     public static String getWeek() {        return formatDate(new Date(), "E");     }       /**      * 日期型字符串转化为日期 格式 { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",      * "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd" }      */     public static Date parseDate(Object str) {        if (str == null) {           return null;        }        try {           return parseDate(str.toString(), parsePatterns);        } catch (ParseException e) {           return null;        }     }       /**      * 获取过去的天数      *       * @param date      * @return      */     public static long pastDays(Date date) {        long t = new Date().getTime() - date.getTime();        return t / (24 * 60 * 60 * 1000);     }       /**      * 获取过去的小时      * @param date      * @return      */     public static long pastHour(Date date) {        long t = new Date().getTime()-date.getTime();        return t/(60*60*1000);     }          /**      * 获取过去的分钟      * @param date      * @return      */     public static long pastMinutes(Date date) {        long t = new Date().getTime()-date.getTime();        return t/(60*1000);     }          /**      * 转换为时间(天,时:分:秒.毫秒)      * @param timeMillis      * @return      */      public static String formatDateTime(long timeMillis){        long day = timeMillis/(24*60*60*1000);        long hour = (timeMillis/(60*60*1000)-day*24);        long min = ((timeMillis/(60*1000))-day*24*60-hour*60);        long s = (timeMillis/1000-day*24*60*60-hour*60*60-min*60);        long sss = (timeMillis-day*24*60*60*1000-hour*60*60*1000-min*60*1000-s*1000);        return (day>0?day+",":"")+hour+":"+min+":"+s+"."+sss;      }          /**      * 获取某一天的开始时间(0点)      * @param date      * @return      */     public static Date getDateStart(Date date) {        if (date == null) {           return null;        }        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        try {           date = sdf.parse(formatDate(date, "yyyy-MM-dd") + " 00:00:00");        } catch (ParseException e) {           e.printStackTrace();        }        return date;     }       /**      * 获取某一天的结束时间(23:59)      *       * @param date      * @return      */     public static Date getDateEnd(Date date) {        if (date == null) {           return null;        }        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        try {           date = sdf.parse(formatDate(date, "yyyy-MM-dd") + " 23:59:59");        } catch (ParseException e) {           e.printStackTrace();        }        return date;     }          /**      * 比较两个日期时间的大小,反回1表示preDateStr > nextDateStr,0就相等,-1为小于      * @author: weihuang.peng      * @param preDateStr      * @param nextDateStr      * @return result      */     public static int compareDate(Object preDateStr, Object nextDateStr) {        int result = 0;        Date preDate = parseDate(preDateStr);        Date nextDate = parseDate(nextDateStr);        try {           result =  preDate.compareTo(nextDate);        } catch (Exception e) {           result = 0;           e.printStackTrace();        }        return result;     }          /**      * 获取某一天的前几天或者后几天,根据数字符号决定天数      * @author: weihuang.peng      * @param date      * @param days      * @return      */     public static String getPastDayStr(Object dateObj, int days) {        Date date = parseDate(dateObj);        long time = date.getTime() + days * (long)(24 * 60 * 60 * 1000);        return formatDate(new Date(time));     }          /**      * preDateStr - nextDateStr 返回秒数      * @author: huiyang.yu      * @param preDateStr      * @param nextDateStr      * @return      */     public static long getSubactDate(Object preDateStr, Object nextDateStr) {        Date preDate = parseDate(preDateStr);        Date nextDate = parseDate(nextDateStr);        long result = (preDate.getTime() - nextDate.getTime()) / 1000L;        return result;      }          /**      * 返回过去的天数: preDateStr - nextDateStr       * @author: weihuang.peng      * @param preDateStr      * @param nextDateStr      * @return      */     public static long getDifferDate(Object preDateStr, Object nextDateStr) {        return getSubactDate(preDateStr, nextDateStr) / (60 * 60 * 24L);     }          /**      * 传入日期时间与当前系统日期时间的比较,      * 若日期相同,则根据时分秒来返回 ,      * 否则返回具体日期      * @author: huiyang.yu      * @param updateDate 传入日期      * @param updateTime 传入时间      * @return 日期或者 xx小时前||xx分钟前||xx秒前      */     public static String getNewUpdateDateString(String updateDate, String updateTime) {        String result = updateDate;        long time = 0;        if (updateDate.equals(DateUtils.getDate())) {           time = DateUtils.getSubactDate(DateUtils.getDateTime(), updateDate                 + " " + updateTime);           if (time >= 3600) {              result = time / 3600 + "小时前";           } else if (time >= 60) {              result = time / 60 + "分钟前";           } else if (time >= 1) {              result = time + "秒前";           } else {              result = "刚刚";           }        } else if (result.length() >= 10) {           result = result.substring(5);        }        return result;     }   }