spring mvc 使用及json 日期轉換解決方案

第一步:創建CustomObjectMapper類

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /**  
  2.  * 解決SpringMVC使用@ResponseBody返回json時,日期格式默認顯示爲時間戳的問題。需配合<mvc:message-converters>使用  
  3.  *   
  4.  * @author hellostory  
  5.  * @date 2013-10-31 下午04:17:52  
  6.  */  
  7. @Component("customObjectMapper")  
  8. public class CustomObjectMapper extends ObjectMapper {  
  9.   
  10.     public CustomObjectMapper() {  
  11.         CustomSerializerFactory factory = new CustomSerializerFactory();  
  12.         factory.addGenericMapping(Date.class, new JsonSerializer<Date>() {  
  13.             @Override  
  14.             public void serialize(Date value, JsonGenerator jsonGenerator,  
  15.                     SerializerProvider provider) throws IOException, JsonProcessingException {  
  16.                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  17.                 jsonGenerator.writeString(sdf.format(value));  
  18.             }  
  19.         });  
  20.         this.setSerializerFactory(factory);  
  21.     }  
  22. }  


第二步:配置如下:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <mvc:annotation-driven>  
  2.     <mvc:message-converters>  
  3.         <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">  
  4.             <property name="objectMapper" ref="customObjectMapper"></property>  
  5.         </bean>  
  6.     </mvc:message-converters>  
  7. </mvc:annotation-driven>  

效果如下:

格式化前:


格式化後:



------------------------------------------------------------------------------------------------------------------------------------------------------------


又到搭新開發環境的時候,總是不免去網上搜下目前最新的框架。spring是web開發必用的框架,於是乎下載了目前最新的spring4.0.3,同時越來越不想用struts2,想試試spring mvc,也將spring-webmvc4.0.3下了下來,投入兩天時間學習後,發現還是挺優雅的,特別是從3.0後,spring mvc使用註解方式配製,以及對rest風格的支持,真是完美致極。
下面將這兩天研究到的問題做個總結,供參考。
1.request對象的獲取
方式1:
在controller方法上加入request參數,spring會自動注入,如:public String list(HttpServletRequest request,HttpServletResponse response)
方式2:在controller類中加入@Resource private HttpServletRequest request 屬性,spring會自動注入,這樣不知道會不會出現線程問題,因爲一個controller實例會爲多個請求服務,暫未測試。
方式3:在controller方法中直接寫代碼獲取 HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
方式4:在controller中加入以下方法,此方法會在執行此controller的處理方法之前執行

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @ModelAttribute  
  2. private void initServlet(HttpServletRequest request,HttpServletResponse response) {  
  3.     //String p=request.getParameter("p");  
  4.     //this.req=request;//實例變量,有線程安全問題,可以使用ThreadLocal模式保存  
  5. }  

2.response對象的獲取

可以參照以上request的獲取方式1和方式4,方式2和方式3對response對象無效!
3.表單提交之數據填充

直接在方法上加入實體對象參數,spring會自動填充對象中的屬性,對象屬性名要與<input>的name一致纔會填充,如:public boolean doAdd(Demo demo)

4.表單提交之數據轉換-Date類型

在實體類的屬性或get方法上加入 @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss"),那麼表單中的日期字符串就會正確的轉換爲Date類型了。還有@NumberFormat註解,暫時沒用,就不介紹了,一看就知道是對數字轉換用的。

5.json數據返回
在方法上加入@ResponseBody,同時方法返回值爲實體對象,spring會自動將對象轉換爲json格式,並返回到客戶端。如下所示:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @RequestMapping("/json1")  
  2. @ResponseBody  
  3. public Demo json1() {  
  4.     Demo demo=new Demo();  
  5.     demo.setBirthday(new Date());  
  6.     demo.setCreateTime(new Date());  
  7.     demo.setHeight(170);  
  8.     demo.setName("tomcat");   
  9.     demo.setRemark("json測試");   
  10.     demo.setStatus((short)1);   
  11.     return demo;  
  12. }  
注意:spring配置文件要加上:<mvc:annotation-driven/>,同時還要引入jackson-core.jar,jackson-databind.jar,jackson-annotations.jar(2.x的包)纔會自動轉換json
這種方式是spring提供的,我們還可以自定義輸出json,以上第二條不是說了獲取response對象嗎,拿到response對象後,任由開發人員宰割,想怎麼返回就怎麼返回。
方法不要有返回值,如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @RequestMapping("/json2")  
  2. public void json2() {  
  3.     Demo demo=new Demo();  
  4.     demo.setBirthday(new Date());  
  5.     demo.setCreateTime(new Date());  
  6.     demo.setHeight(170);  
  7.     demo.setName("tomcat");  
  8.     demo.setRemark("json測試");  
  9.     demo.setStatus((short)1);  
  10.     String json=JsonUtil.toJson(obj);//;json處理工具類  
  11.     HttpServletResponse response = //獲取response對象  
  12.     response.getWriter().print(json);  
  13. }  
OK,一切很完美。接着噁心的問題迎面而來,date類型轉換爲json字符串時,返回的是long time值,如果你想返回“yyyy-MM-dd HH:mm:ss”格式的字符串,又要自定義了。我很奇怪,不是有@DateTimeFormat註解嗎,爲什麼不利用它。難道@DateTimeFormat只在表單提交時,將字符串轉換爲date類型,而date類型轉換爲json字符串時,就不用了。帶着疑惑查源碼,原來spring使用jackson轉換json字符,而@DateTimeFormat是spring-context包中的類,jackson如何轉換,spring不方便作過多幹涉,於是只能遵守jackson的轉換規則,自定義日期轉換器。
先寫一個日期轉換器,如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class JsonDateSerializer extends JsonSerializer<Date> {  
  2.    private SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  3.    @Override  
  4.    public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)  
  5.            throws IOException, JsonProcessingException {  
  6.        String value = dateFormat.format(date);  
  7.        gen.writeString(value);  
  8.    }  
  9. }  
在實體類的get方法上配置使用轉換器,如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")  
  2. @JsonSerialize(using=JsonDateSerializer.class)  
  3. public Date getCreateTime() {  
  4.     return this.createTime;  
  5. }  
OK,到此搞定。
你真的滿意了嗎,這麼不優雅的解決方案,假設birthday屬性是這樣的,只有年月日,無時分秒
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @DateTimeFormat(pattern="yyyy-MM-dd")  
  2. public Date getBirthday() {  
  3.     return this.birthday;  
  4. }  
這意味着,又要爲它定製一個JsonDate2Serializer的轉換器,然後配置上,像這樣
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @DateTimeFormat(pattern="yyyy-MM-dd")  
  2. @JsonSerialize(using=JsonDate2Serializer.class)  
  3. public Date getBirthday() {  
  4.     return this.birthday;  
  5. }  
假設還有其它格式的Date字段,還得要爲它定製另一個轉換器。my god,請饒恕我的罪過,不要讓我那麼難受
經過分析源碼,找到一個不錯的方案,此方案將不再使用@JsonSerialize,而只利用@DateTimeFormat配置日期格式,jackson就可以正確轉換,但@DateTimeFormat只能配置在get方法上,這也沒什麼關係。
先引入以下類,此類對jackson的ObjectMapper類做了註解掃描攔截,使它也能對加了@DateTimeFormat的get方法應用日期格式化規則
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.xxx.utils;  
  2.   
  3. import java.io.IOException;  
  4. import java.lang.reflect.AnnotatedElement;  
  5. import java.text.SimpleDateFormat;  
  6. import java.util.Date;  
  7. import org.springframework.format.annotation.DateTimeFormat;  
  8. import org.springframework.stereotype.Component;  
  9. import com.fasterxml.jackson.core.JsonGenerator;  
  10. import com.fasterxml.jackson.core.JsonProcessingException;  
  11. import com.fasterxml.jackson.databind.JsonSerializer;  
  12. import com.fasterxml.jackson.databind.ObjectMapper;  
  13. import com.fasterxml.jackson.databind.SerializerProvider;  
  14. import com.fasterxml.jackson.databind.introspect.Annotated;  
  15. import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;  
  16. import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;  
  17.   
  18. /** 
  19.  * json處理工具類 
  20.  * @author zhangle 
  21.  */  
  22. @Component  
  23. public class JsonUtil {  
  24.   
  25.         private static final String DEFAULT_DATE_FORMAT="yyyy-MM-dd HH:mm:ss";  
  26.         private static final ObjectMapper mapper;  
  27.           
  28.         public ObjectMapper getMapper() {  
  29.                 return mapper;  
  30.         }  
  31.   
  32.         static {  
  33.                 SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);  
  34.                   
  35.                 mapper = new ObjectMapper();  
  36.                 mapper.setDateFormat(dateFormat);  
  37.                 mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {  
  38.                         @Override  
  39.                         public Object findSerializer(Annotated a) {  
  40.                                 if(a instanceof AnnotatedMethod) {  
  41.                                         AnnotatedElement m=a.getAnnotated();  
  42.                                         DateTimeFormat an=m.getAnnotation(DateTimeFormat.class);  
  43.                                         if(an!=null) {  
  44.                                                 if(!DEFAULT_DATE_FORMAT.equals(an.pattern())) {  
  45.                                                         return new JsonDateSerializer(an.pattern());  
  46.                                                 }  
  47.                                         }  
  48.                                 }  
  49.                                 return super.findSerializer(a);  
  50.                         }  
  51.                 });  
  52.         }  
  53.           
  54.         public static String toJson(Object obj) {  
  55.                 try {  
  56.                         return mapper.writeValueAsString(obj);  
  57.                 } catch (Exception e) {  
  58.                         throw new RuntimeException("轉換json字符失敗!");  
  59.                 }  
  60.         }  
  61.           
  62.         public <T> T toObject(String json,Class<T> clazz) {  
  63.                 try {  
  64.                         return mapper.readValue(json, clazz);  
  65.                 } catch (IOException e) {  
  66.                         throw new RuntimeException("將json字符轉換爲對象時失敗!");  
  67.                 }  
  68.         }  
  69.           
  70.         public static class JsonDateSerializer extends JsonSerializer<Date>{  
  71.             private SimpleDateFormat dateFormat;  
  72.             public JsonDateSerializer(String format) {  
  73.                  dateFormat = new SimpleDateFormat(format);  
  74.                 }  
  75.               
  76.             @Override  
  77.             public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)  
  78.                     throws IOException, JsonProcessingException {  
  79.                 String value = dateFormat.format(date);  
  80.                 gen.writeString(value);  
  81.             }  
  82.         }  
  83. }  
再將<mvc:annotation-driven/>改爲以下配置,配置一個新的json轉換器,將它的ObjectMapper對象設置爲JsonUtil中的objectMapper對象,此轉換器比spring內置的json轉換器優先級更高,所以與json有關的轉換,spring會優先使用它。
[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <mvc:annotation-driven>  
  2.     <mvc:message-converters>  
  3.         <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">  
  4.             <property name="objectMapper" value="#{jsonUtil.mapper}"/>  
  5.             <property name="supportedMediaTypes">  
  6.                 <list>  
  7.                     <value>text/json;charset=UTF-8</value>  
  8.                 </list>  
  9.             </property>    
  10.         </bean>  
  11.     </mvc:message-converters>  
  12. </mvc:annotation-driven>  

接下來就可以這樣配置實體類,jackson也能正確轉換Date類型

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")  
  2. public Date getCreateTime() {  
  3.     return this.createTime;  
  4. }  
  5. @DateTimeFormat(pattern="yyyy-MM-dd")  
  6. public Date getBirthday() {  
  7.     return this.birthday;  
  8. }  

完畢,一切都完美了。


以下爲2014/4/21 補充

寫了那麼多,發現白忙活了一場,原來jackson也有一個@JsonFormat註解,將它配置到Date類型的get方法上後,jackson就會按照配置的格式轉換日期類型,而不自定義轉換器類,欲哭無淚啊。辛苦了那麼多,其實別人早已提供,只是沒有發現而已。

不說了,直接上方案吧。

1.spring配置照樣是這樣:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <mvc:annotation-driven>  

2.JsonUtil可以不用了,但如果要自己從response對象輸出json,那麼還是可以用,但改成了這樣

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.xxx.utils;  
  2.   
  3. import java.io.IOException;  
  4. import java.text.SimpleDateFormat;  
  5. import org.springframework.stereotype.Component;  
  6. import com.fasterxml.jackson.databind.ObjectMapper;  
  7.   
  8. /** 
  9.  * json處理工具類 
  10.  * @author zhangle 
  11.  */  
  12. @Component  
  13. public class JsonUtil {  
  14.   
  15.     private static final String DEFAULT_DATE_FORMAT="yyyy-MM-dd HH:mm:ss";  
  16.     private static final ObjectMapper mapper;  
  17.   
  18.     static {  
  19.         SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);  
  20.         mapper = new ObjectMapper();  
  21.         mapper.setDateFormat(dateFormat);  
  22.     }  
  23.       
  24.     public static String toJson(Object obj) {  
  25.         try {  
  26.             return mapper.writeValueAsString(obj);  
  27.         } catch (Exception e) {  
  28.             throw new RuntimeException("轉換json字符失敗!");  
  29.         }  
  30.     }  
  31.       
  32.     public <t> T toObject(String json,Class<t> clazz) {  
  33.         try {  
  34.             return mapper.readValue(json, clazz);  
  35.         } catch (IOException e) {  
  36.             throw new RuntimeException("將json字符轉換爲對象時失敗!");  
  37.         }  
  38.     }  
  39. }</t></t>  

3.實體類的get方法就需要多一個@JsonFormat的註解配置

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")  
  2. @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")  
  3. public Date getCreateTime() {  
  4. return this.createTime;  
  5. }  
  6. @DateTimeFormat(pattern="yyyy-MM-dd")  
  7. @JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8")  
  8. public Date getBirthday() {  
  9.     return this.birthday;  
  10. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章