首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Struts2 日期類型轉換失效問題:Locale 導致 yyyy-MM-dd 解析失敗的根因分析與解決方案

     編輯:Java 綜合教學

Struts2 日期類型轉換失效:Locale 陷阱深度剖析

項目運行時突然拋出異常:

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

異常發生在攔截器階段,Action 尚未進入。功能前期測試完全正常,排查半天無果,最終同事協助定位:IE 瀏覽器語言環境被改為英語,導致 Struts2 默認日期格式解析失敗。

問題複現鏈路

  1. 頁面提交日期字符串 yyyy-MM-dd(如 2023-10-01)。
  2. Struts2 類型轉換器嘗試將 String 轉為 java.util.Date。
  3. 轉換器內部根據 Locale 選擇解析格式,英語環境下 SHORT 格式為 M/d/yy,無法匹配 yyyy-MM-dd。
  4. 轉換失敗,字符串原樣傳遞給 Action,觸發「找不到 setSubDate(String[])」異常。

源碼級根因:XWorkBasicConverter.doConvertToDate

Struts2 內部按優先級嘗試多種 DateFormat:

private DateFormat[] getDateFormats(Locale locale) {
    DateFormat[] dfs = {
        DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale),
        DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale),
        DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale),
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), // RFC3339 硬編碼
        DateFormat.getDateInstance(DateFormat.SHORT, locale),   // 關鍵:英語下為 M/d/yy
        DateFormat.getDateInstance(DateFormat.MEDIUM, locale),
        DateFormat.getDateInstance(DateFormat.LONG, locale)
    };
    return dfs;
}

英語 Locale.US 下,DateFormat.getDateInstance(SHORT) 返回 M/d/yy,完全不匹配 yyyy-MM-dd。前六種格式均失敗,最終落回硬編碼的 RFC3339 格式(含 'T'),仍不匹配,拋出 ParseException,轉換失敗。

解決方案

方案一:自定義類型轉換器(推薦,不依賴客戶端 Locale)

public class DateConverter extends DefaultTypeConverter {
    private static final String[] PATTERNS = {"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM"};

    @Override
    public Object convertValue(Map context, Object value, Class toType) {
        if (toType == Date.class && value instanceof String) {
            String str = (String) value;
            return DateUtils.parseDate(str, PATTERNS); // Commons Lang3
        } else if (toType == String.class && value instanceof Date) {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value);
        }
        return null;
    }
}

在 classpath 下創建 xwork-conversion.properties 註冊全局生效:

java.util.Date=com.yourpkg.DateConverter

方案二:強制設置 Action 上下文 Locale(僅影響當前請求)

ActionContext.getContext().setLocale(Locale.CHINA);

方案三:前端統一提交 RFC3339 格式(yyyy-MM-dd'T'HH:mm:ss)

但需前端配合,且不利於用戶直接輸入。

最佳實踐建議

  • 永不依賴默認 Locale 進行日期解析,生產環境必須註冊自定義轉換器。
  • 轉換器中支持多種常用格式(yyyy-MM-dd、yyyy-MM-dd HH:mm:ss、時間戳毫秒數),兼容性最強。
  • 避免在模型層使用 java.sql.Date 繞過轉換,破壞領域模型純粹性。
參考資源:Struts2 類型轉換 Wiki、IE Locale 導致日期解析失敗案例
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved