Struts2中Date日期轉換的問題

来源:http://www.cnblogs.com/aspirant/archive/2016/12/12/6165694.html
-Advertisement-
Play Games

今天跑程式的時候莫名其妙的出現了下麵的一個異常: java.lang.NoSuchMethodException:com.ca.agent.model.mybatis.ApprovalInforCangra.setSubDate([Ljava.lang.String;) 這類異常信息在以前是處理過的 ...


 

今天跑程式的時候莫名其妙的出現了下麵的一個異常:

java.lang.NoSuchMethodException:com.ca.agent.model.mybatis.ApprovalInforCangra.setSubDate([Ljava.lang.String;)

這類異常信息在以前是處理過的,按照以前的思路在eclipse的調試模式下看看具體的情況,不過這次比較奇怪,根本沒進入到對應的Action類中就拋出了此異常信息,進一步查找發現,在調用攔截器方法的時候就拋出了此異常信息,還有沒有想明白的就是對應的功能我是測試過好多次的,之前這一塊是沒有任何問題的!不過現在事實是卻拋出了異常信息,一定是那裡出錯了,到底是那裡哪?查找了大半個下午也沒找到對應的解決方案,應該是我忽略了什麼地方,一時半會可能也想不起來了,找位同事幫忙吧!於是找來同事G來幫忙,最終找到了問題的原因!Struts2預設可支持String和Date的轉換,但僅支持短格式和本地化有關日期格式轉換,前幾天我將自己的IE瀏覽器的語言環境改成了英語的!而對於yyyy-MM-dd這種日期類型,在英語語言中是沒法匹配的,由於Struts2匹配日期時,使用了Locale,所以使用英文的語言環境,就導致了此問題的出現,把中文設置為預設語言環境後,再測試,沒問題了!

轉載自:http://polaris.blog.51cto.com/1146394/315403

Web開發會涉及到很多類型轉換的情況。我們知道,頁面中的一切值都是字元串類型,而到後臺,我們需要的可能是其他各種類型;同時,頁面顯示也是字元串類型。這就涉及到Web中基本的類型轉換問題:從String轉換為各種類型與從各種類型轉換為String類型。

在Java Web開發中,進行上述轉換一般有以下幾種:
1、在Servlet中,這一切的轉換我們得自己寫代碼完成;
2、在Struts1.x中,我們通過apache commons-beanutils中的converters來幫助完成這些事情;
3、在Struts2中,使用的則是基於ongl的類型轉換;
……

由 於類型轉換的通用性,因而Web框架都會實現大多數類型的轉換功能,而不需要程式員編碼實現。然而,對於java.util.Date這種類型的轉換,各 大框架似乎做得都不盡如人意。如:在Struts1.x中,該類型的轉換就會有問題,很多人建議使用java.sql.Date這種類型來解決日期轉換的 問題(實際上可以自定義一個類型轉換器來解決該問題)。在Struts2中,這個問題似乎依然存在,也許你從來沒有遇到過。的確,一般人確實不會遇到,會 覺得沒有這個問題。下麵就是我遇到的問題與解決方法。

日期類型的轉換,Web開發中幾乎都會遇到,我現在做得項目也不例外。在開發的過程 中,也許就像你一樣,我沒有對日期類型的轉換做任何特殊的處理,而且Struts2也很好的幫我完成了轉換。然而同事測試的時候卻出現了一個“莫名其妙” 的問題:輸入一個常用格式的日期類型yyyy-MM-dd,到後臺卻報錯:找不到對應的set方法—— setEffDate(Ljava.lang.String)。的確,程式中只有setEffDate(java.util.Date)這個方法,沒有 setEffDate(Ljava.lang.String)這個方法。從Ljava.lang.String可以看出,傳到後臺的String類型並沒 有轉換成Date類型,因而報錯。

一開始,我以為是我UT沒做好,於是在自己的電腦上模擬同事的測試,結果一點問題也沒有。這就奇怪了。 經過自己分析,覺得可能是IE瀏覽器的原因,因為同事測試用的是IE,而我用的是FireFox。於是在自己的機子上用IE測試,同時在同事機子上用 FireFox測試,結果這兩次測試都沒有出現上面的問題。雖然沒有找到問題所在,但可以初步肯定:IE的問題,但似乎又不完全是IE的問題,因為在我的 電腦上的IE(版本與同事一樣,都是IE6)沒有上述問題。這就奇怪了,是什麼問題呢,真是百思不得其解。

這個時候,我想起了之前遇到的 一個不解得情況:從後臺獲得的日期類型在頁面上顯示時,跟上面情況一樣,在同事的IE中,日期顯示的格式竟然是:yyyy-MM- ddTHH:mm:ss。多了一個T,真是莫名其妙,而且只在同事的IE瀏覽器中出現(當時解決方法是在JS中將'T'替換為空格,沒有去深究,但現在必 須的深究了)。yyyy-MM-ddTHH:mm:ss這種日期格式有嗎?於是查詢JDK:在SimpleDateFormat類中找到了該日期格式,這 種格式是“美國語言環境中日期和時間的模式之一”。原來還真有這種格式。竟然這是美國語言中使用的日期格式,而Struts2是美國人開發的,也許跟這個 有點關係。於是查看Struts2中關於Date類型轉換的源碼。
在XWorkBasicConverter類中

private Object doConvertToDate(Map<String, Object> context, Object value, Class toType) {
                Date result = null;

                if (value instanceof String && value != null && ((String) value).length() > 0) {
                        String sa = (String) value;
                        Locale locale = getLocale(context);

                        DateFormat df = null;
                        if (java.sql.Time.class == toType) {
                                df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
                        } else if (java.sql.Timestamp.class == toType) {
                                Date check = null;
                                SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
                                                DateFormat.MEDIUM,
                                                locale);
                                SimpleDateFormat fullfmt = new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
                                                locale);

                                SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
                                                locale);

                                SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
                                for (SimpleDateFormat fmt : fmts) {
                                        try {
                                                check = fmt.parse(sa);
                                                df = fmt;
                                                if (check != null) {
                                                        break;
                                                }
                                        } catch (ParseException ignore) {
                                        }
                                }
                        } else if (java.util.Date.class == toType) {
                                Date check = null;
                                DateFormat[] dfs = getDateFormats(locale);
                                for (DateFormat df1 : dfs) {
                                        try {
                                                check = df1.parse(sa);
                                                df = df1;
                                                if (check != null) {
                                                        break;
                                                }
                                        }
                                        catch (ParseException ignore) {
                                        }
                                }
                        }
                        //final fallback for dates without time
                        if (df == null) {
                                df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
                        }
                        try {
                                df.setLenient(false); // let's use strict parsing (XW-341)
                                result = df.parse(sa);
                                if (!(Date.class == toType)) {
                                        try {
                                                Constructor constructor = toType.getConstructor(new Class[]{long.class});
                                                return constructor.newInstance(new Object[]{Long.valueOf(result.getTime())});
                                        } catch (Exception e) {
                                                throw new XWorkException("Couldn't create class " + toType + " using default (long) constructor", e);
                                        }
                                }
                        } catch (ParseException e) {
                                throw new XWorkException("Could not parse date", e);
                        }
                } else if (Date.class.isAssignableFrom(value.getClass())) {
                        result = (Date) value;
                }
                return result;
        }

        private DateFormat[] getDateFormats(Locale locale) {
                DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
                DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
                DateFormat dt3 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);

                DateFormat d1 = DateFormat.getDateInstance(DateFormat.SHORT, locale);
                DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
                DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale);

                DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

                DateFormat[] dfs = {dt1, dt2, dt3, rfc3399, d1, d2, d3}; //added RFC 3339 date format (XW-473)
                return dfs;
        }


其中SHORT、MEDIUM、LONG在JDK中的DateFormat類中有說明。
從 這句DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");可以看出,Struts2硬編碼使用了這樣一種格式。然 而,Struts2中這種格式是放在最後的,為啥只有同事的IE瀏覽器測試時使用的是這種格式呢?(在調試時,用同時IE,日期輸入中按這種格式輸入,還 真的沒有問題了)這說明,同事的電腦中,前面六種DateFormat都沒有匹配,查看DateFormat中關於SHORT、MEDIUM、LONG的 說明,可以知道,對於yyyy-MM-dd這種日期類型,在英語語言中是沒法匹配的,由於Struts2匹配日期時,使用了Locale,可見,同事的IE瀏覽器預設的語言環境是英語。一經查看,果然如此,把中文設置為預設語言環境,再測試,沒問題了。終於知道了原因。

個人覺得,Struts2中,最後一種日期模式寫死成美國標準,不是很好。

針 對這個問題,我們沒法要求客戶一定設置中文為預設瀏覽器的語言環境。因而對於Date類型的轉換,可以自己定義一個轉換器。以下來自 http://www.javaeye.com/wiki/struts2/1365-passing-parameters-in-struts2 中的一個類型轉換器定義(不適合國際化的環境),如需要,你可以定義自己的轉換器:


public class DateConverter extends DefaultTypeConverter {

        private static final Logger logger = Logger.getLogger(DateConverter.class);

        private static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

        private static final String DATE_PATTERN = "yyyy-MM-dd";
        
        private static final String MONTH_PATTERN = "yyyy-MM";

        /**
         * Convert value between types
         */
        @SuppressWarnings("unchecked")
        public Object convertValue(Map ognlContext, Object value, Class toType) {
                Object result = null;
                if (toType == Date.class) {
                        result = doConvertToDate(value);
                } else if (toType == String.class) {
                        result = doConvertToString(value);
                }
                return result;
        }

        /**
         * Convert String to Date
         *
         * @param value
         * @return
         */
        private Date doConvertToDate(Object value) {
                Date result = null;

                if (value instanceof String) {
                        result = DateUtils.parseDate((String) value, new String[] { DATE_PATTERN, DATETIME_PATTERN, MONTH_PATTERN });

                        // all patterns failed, try a milliseconds constructor
                        if (result == null && StringUtils.isNotEmpty((String)value)) {

                                try {
                                        result = new Date(new Long((String) value).longValue());
                                } catch (Exception e) {
                                        logger.error("Converting from milliseconds to Date fails!");
                                        e.printStackTrace();
                                }

                        }

                } else if (value instanceof Object[]) {
                        // let's try to convert the first element only
                        Object[] array = (Object[]) value;

                        if ((array != null) && (array.length >= 1)) {
                                value = array[0];
                                result = doConvertToDate(value);
                        }

                } else if (Date.class.isAssignableFrom(value.getClass())) {
                        result = (Date) value;
                }
                return result;
        }

        /**
         * Convert Date to String
         *
         * @param value
         * @return
         */
        private String doConvertToString(Object value) {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATETIME_PATTERN);
                String result = null;
                if (value instanceof Date) {
                        result = simpleDateFormat.format(value);
                }
                return result;
        }
}


可以將該轉換器註冊為全局的:在classpath下建立xwork-conversion.properties文件,內容為:java.util.Date=你的類型轉換器的完整限定類名

參看:

http://ollevere.iteye.com/blog/1431947

http://www.jb51.net/article/35465.htm

http://struts2.group.iteye.com/group/wiki/1365-passing-parameters-in-struts2


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第一篇博客。 克魯斯卡爾求最小生成樹思想:首先將n個點看做n個獨立的集合,將所有邊快排(從小到大)。然後,按排好的順序枚舉每一條邊,判斷這條邊連接的兩個點是否屬於一個集合。若是,則將這條邊加入最小生成樹,並將兩個點所在的集合合併為一個集合。若否,則跳過。直到找到n-1條邊為止。 #include<i ...
  • 這個系統是南方七星彩投註網站系統源碼,網站是採用php+MySQL的。基本實現功能如下:這個是普通的七星前四位的網投註平臺股東-總代理-代理-會員 這四個級別 網站大家只限於學習與交流,並且在合法的範圍使用,為了防止系統其他用戶,代碼有進行加密了,不便多多瞭解。 投註網站源碼附件: http://f ...
  • 對於一個有登錄限制(許可權限制)的網站,用戶輸入身份驗證信息以後,驗證成功後跳轉到登錄前的頁面是一項很人性化的功能。那麼獲取登錄前的頁面地址就很關鍵,今天在做一個yii2項目的登錄調試時發現了一些很有意思的問題,記錄下來。 1,場景描述 網站SiteA上的頁面Page2需要登錄後才能查看,Page2的 ...
  • ...
  • 列印一排*,很簡單,列印下圖 也很簡單,代碼如下: 可是昨天想了好久都沒想到怎樣做到下麵圖片的樣子,今天突然就有了靈感 代碼很簡單,就是昨天想破了腦袋都想不出來,好笨啊我 第一行列印一個*,第二行行列印兩個*,大三行列印三個*,這樣分析就找到規律了,定義一個a=1,外層迴圈實現列印幾行,定義一個i= ...
  • 上面的代碼是段合法的cpp代碼嗎? 答案當然是是的. 這些 問號 是個什麼鬼?它們就是cpp標準中定義的 "Trigraph(MS)" .之所以出現這些神奇的符號,主因主要是字元集的問題.簡單來說可以理解為某些老外的鍵盤沒有"{","|","\"這些符號.所以需要用這種類似"轉義字元"的東東來表達c ...
  • KMP演算法是字元串模式匹配當中最經典的演算法,原來大二學數據結構的有講,但是當時只是記住了原理,但不知道代碼實現,今天終於是完成了KMP的代碼實現。原理KMP的原理其實很簡單,給定一個字元串和一個模式串,然後找模式串在給定字元串中的位置。將兩個字元串轉換為字元數組,然後從兩個數組的開始位置"i","j ...
  • Error Handling with Exceptions ___ The ideal time to catch an error is at compile time, before you even try to run the program. The rest of the proble ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...