這篇文章主要介紹“SpringMVC實現(xiàn)日期格式屬性自動轉(zhuǎn)成時間戳”,在日常操作中,相信很多人在SpringMVC實現(xiàn)日期格式屬性自動轉(zhuǎn)成時間戳問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”SpringMVC實現(xiàn)日期格式屬性自動轉(zhuǎn)成時間戳”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
創(chuàng)新互聯(lián)是一家專注于網(wǎng)站建設(shè)、網(wǎng)站設(shè)計與策劃設(shè)計,定安網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:定安等地區(qū)。定安做網(wǎng)站價格咨詢:028-86922220
SpringMVC搭建的微服務(wù)系統(tǒng),后端數(shù)據(jù)庫對時間類型的存儲使用的是Long類型,而前端框架傾向于使用yyyy-MM-dd HH:mm:ss這種標(biāo)準(zhǔn)顯示格式,前端JSON格式的請求報文與后臺的接口交互都需要進行格式轉(zhuǎn)換,這部分轉(zhuǎn)換功能由后臺實現(xiàn)。
使用時我們發(fā)現(xiàn),前端定義的JSON請求,時間格式為yyyy-MM-dd HH:mm:ss,如果后臺定義的POJO相應(yīng)的屬性為Long類型,可以自動轉(zhuǎn)換為時間戳,對此非常好奇,框架是如何實現(xiàn)這一功能的?
spring boot 2.1.6.RELEASE
spring cloud Greenwich.SR3
alibaba fastjson 1.2.60
注意json框架使用的是fastjson
為了方便演示,定義一個特別簡單的POJO類:
public class DateReq { private String dateFormat; private Long timestamp; // 省略getter/setter/toString方法 }
再定義一個簡單的Controller方法:
@RestController public class DemoController { @PostMapping(value = "/json/demo/info") public ApiResponse> dateJson(@RequestBody DateReq request) { System.out.println(request); } }
請求報文如下:
{ "dateFormat": "2020-08-07 18:50:00", "timestamp": "2020-08-07 18:50:00" }
響應(yīng)的結(jié)果:DateReq{dateFormat='2020-08-07 18:50:00', timestamp=1596797400000}
從結(jié)果可以發(fā)現(xiàn),dateFormat字段我們定義的是String類型,timestamp定義的是Long類型,請求報文兩個字段使用相同的值,但是到了Controller方法里,timestamp自動變成Long類型的時間戳了,并且是按東8區(qū)轉(zhuǎn)換的。
在這里我們可以得到一個使用經(jīng)驗:POJO的時間格式是可以自動轉(zhuǎn)換成Long類型時間戳的,默認時區(qū)取操作系統(tǒng)的時區(qū),或者通過jvm參數(shù)-Duser.timezone=GMT+08
設(shè)置。
既然看到了自動轉(zhuǎn)換的效果,非常好奇框架是怎么實現(xiàn)的,我們通過斷點查找堆棧:
deserialze:79, LongCodec (com.alibaba.fastjson.serializer) parseField:85, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer) parseField:1224, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:850, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) parseRest:1538, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:-1, FastjsonASMDeserializer_3_DateReq (com.alibaba.fastjson.parser.deserializer) deserialze:284, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) parseObject:692, DefaultJSONParser (com.alibaba.fastjson.parser) parseObject:383, JSON (com.alibaba.fastjson) parseObject:448, JSON (com.alibaba.fastjson) parseObject:556, JSON (com.alibaba.fastjson) readType:263, FastJsonHttpMessageConverter (com.alibaba.fastjson.support.spring) read:237, FastJsonHttpMessageConverter (com.alibaba.fastjson.support.spring) readWithMessageConverters:204, AbstractMessageConverterMethodArgumentResolver (org.springframework.web.servlet.mvc.method.annotation) readWithMessageConverters:157, RequestResponseBodyMethodProcessor (org.springframework.web.servlet.mvc.method.annotation) resolveArgument:130, RequestResponseBodyMethodProcessor (org.springframework.web.servlet.mvc.method.annotation) resolveArgument:124, HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
發(fā)現(xiàn)了兩處有價值的信息:
觸發(fā)消息類型轉(zhuǎn)換類是FastJsonHttpMessageConverter
真正完成類型映射是fastjson框架
有這個思路,閱讀源碼時可以把重點放在fastjson上,從JSON反序列化為POJO,Long類型字段處理,找到這段代碼:
publicT deserialze(DefaultJSONParser parser, Type clazz, Object fieldName) { JSONLexer lexer = parser.lexer; Long longObject; try { int token = lexer.token(); if (token == 2) { long longValue = lexer.longValue(); lexer.nextToken(16); longObject = longValue; } else if (token == 3) { BigDecimal number = lexer.decimalValue(); longObject = TypeUtils.longValue(number); lexer.nextToken(16); } else { if (token == 12) { JSONObject jsonObject = new JSONObject(true); parser.parseObject(jsonObject); longObject = TypeUtils.castToLong(jsonObject); } else { Object value = parser.parse(); // 關(guān)注這一行,yyyy-MM-dd HH:mm:ss會執(zhí)行這一行代碼 longObject = TypeUtils.castToLong(value); } if (longObject == null) { return null; } } } catch (Exception var9) { throw new JSONException("parseLong error, field : " + fieldName, var9); } return clazz == AtomicLong.class ? new AtomicLong(longObject) : longObject; }
重點關(guān)注longObject = TypeUtils.castToLong(value);
,yyyy-MM-dd HH:mm:ss
格式的數(shù)據(jù)會執(zhí)行這一行代碼,跟進去查看源碼:
public static Long castToLong(Object value) { if (value == null) { return null; } else if (value instanceof BigDecimal) { return longValue((BigDecimal)value); } else if (value instanceof Number) { return ((Number)value).longValue(); } else { if (value instanceof String) { String strVal = (String)value; if (strVal.length() == 0 || "null".equals(strVal) || "NULL".equals(strVal)) { return null; } if (strVal.indexOf(44) != 0) { strVal = strVal.replaceAll(",", ""); } try { return Long.parseLong(strVal); } catch (NumberFormatException var4) { // 在異常里做最后的掙扎,今天的案例是執(zhí)行到這里的 JSONScanner dateParser = new JSONScanner(strVal); Calendar calendar = null; if (dateParser.scanISO8601DateIfMatch(false)) { calendar = dateParser.getCalendar(); } dateParser.close(); if (calendar != null) { return calendar.getTimeInMillis(); } } } if (value instanceof Map) { Map map = (Map)value; if (map.size() == 2 && map.containsKey("andIncrement") && map.containsKey("andDecrement")) { Iterator iter = map.values().iterator(); iter.next(); Object value2 = iter.next(); return castToLong(value2); } } throw new JSONException("can not cast to long, value : " + value); } }
可以看到在castToLong方法里,對假想的數(shù)據(jù)類型做各種假設(shè)處理,很不幸的是我們試驗的數(shù)據(jù)格式,是在NumberFormatException異常里完成的最后掙扎,使用JSONScanner類接收的請求數(shù)據(jù)。
可以看到在這里通過調(diào)用dateParser.scanISO8601DateIfMatch
對數(shù)據(jù)進行解析,得到calendar對象實例,最終通過calendar獲取時間戳,scanISO8601DateIfMatch方法邏輯很復(fù)雜,總共有450多行,這里截取了其中一部分展現(xiàn)一下:
private boolean scanISO8601DateIfMatch(boolean strict, int rest) { if (rest < 8) { return false; } char c0 = charAt(bp); char c1 = charAt(bp + 1); char c2 = charAt(bp + 2); char c3 = charAt(bp + 3); char c4 = charAt(bp + 4); char c5 = charAt(bp + 5); char c6 = charAt(bp + 6); char c7 = charAt(bp + 7); if ((!strict) && rest > 13) { char c_r0 = charAt(bp + rest - 1); char c_r1 = charAt(bp + rest - 2); } char c10; if (rest < 9) { return false; } char c8 = charAt(bp + 8); char c9 = charAt(bp + 9); int date_len = 10; char y0, y1, y2, y3, M0, M1, d0, d1; if ((c4 == '-' && c7 == '-') // cn || (c4 == '/' && c7 == '/') // tw yyyy/mm/dd ) { y0 = c0; y1 = c1; y2 = c2; y3 = c3; M0 = c5; M1 = c6; d0 = c8; d1 = c9; } else if ((c4 == '-' && c6 == '-') // cn yyyy-m-dd ) { y0 = c0; y1 = c1; y2 = c2; y3 = c3; M0 = '0'; M1 = c5; if (c8 == ' ') { d0 = '0'; d1 = c7; date_len = 8; } else { d0 = c7; d1 = c8; date_len = 9; } } else if ((c2 == '.' && c5 == '.') // de dd.mm.yyyy || (c2 == '-' && c5 == '-') // in dd-mm-yyyy ) { d0 = c0; d1 = c1; M0 = c3; M1 = c4; y0 = c6; y1 = c7; y2 = c8; y3 = c9; } else if (c8 == 'T') { y0 = c0; y1 = c1; y2 = c2; y3 = c3; M0 = c4; M1 = c5; d0 = c6; d1 = c7; date_len = 8; } else { if (c4 == '年' || c4 == '?') { y0 = c0; y1 = c1; y2 = c2; y3 = c3; if (c7 == '月' || c7 == '?') { M0 = c5; M1 = c6; if (c9 == '日' || c9 == '?') { d0 = '0'; d1 = c8; } else if (charAt(bp + 10) == '日' || charAt(bp + 10) == '?'){ d0 = c8; d1 = c9; date_len = 11; } else { return false; } } else if (c6 == '月' || c6 == '?') { M0 = '0'; M1 = c5; if (c8 == '日' || c8 == '?') { d0 = '0'; d1 = c7; } else if (c9 == '日' || c9 == '?'){ d0 = c7; d1 = c8; } else { return false; } } else { return false; } } else { return false; } } if (!checkDate(y0, y1, y2, y3, M0, M1, d0, d1)) { return false; } setCalendar(y0, y1, y2, y3, M0, M1, d0, d1); char t = charAt(bp + date_len); if (charAt(bp + date_len + 3) != ':') { return false; } if (charAt(bp + date_len + 6) != ':') { return false; } char h0 = charAt(bp + date_len + 1); char h2 = charAt(bp + date_len + 2); char m0 = charAt(bp + date_len + 4); char m1 = charAt(bp + date_len + 5); char s0 = charAt(bp + date_len + 7); char s1 = charAt(bp + date_len + 8); if (!checkTime(h0, h2, m0, m1, s0, s1)) { return false; } setTime(h0, h2, m0, m1, s0, s1); char dot = charAt(bp + date_len + 9); int millisLen = -1; // 有可能沒有毫秒?yún)^(qū)域,沒有毫秒?yún)^(qū)域的時候下一個字符位置有可能是'Z'、'+'、'-' int millis = 0; calendar.set(Calendar.MILLISECOND, millis); int timzeZoneLength = 0; char timeZoneFlag = charAt(bp + date_len + 10 + millisLen); if (timeZoneFlag == ' ') { millisLen++; timeZoneFlag = charAt(bp + date_len + 10 + millisLen); } char end = charAt(bp + (date_len + 10 + millisLen + timzeZoneLength)); if (end != EOI && end != '"') { return false; } ch = charAt(bp += (date_len + 10 + millisLen + timzeZoneLength)); token = JSONToken.LITERAL_ISO8601_DATE; return true; }
支持的格式還是挺多,不過基本上符合國內(nèi)的日期使用習(xí)慣,像2020-08-08和2020/08/08,甚至2020年08月08日都行,解析的思路是按位截取判斷,然后作為Calendar的參數(shù),上述節(jié)選的代碼有刪節(jié),有興趣可以查看原代碼。
到此,關(guān)于“SpringMVC實現(xiàn)日期格式屬性自動轉(zhuǎn)成時間戳”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
文章名稱:SpringMVC實現(xiàn)日期格式屬性自動轉(zhuǎn)成時間戳
標(biāo)題路徑:http://weahome.cn/article/pgoogg.html