SpringMvc 請求中日期類型參數接收一二事兒

来源:https://www.cnblogs.com/lvbinbin2yujie/archive/2019/02/23/10415870.html
-Advertisement-
Play Games

首先說明:以版本為Spring 4.3.0為測試對象; 開啟<mvc:annotation-driven /> 測試場景一:請求中含有date屬性,該類型為日期類型,SpringMvc採用@RequestParam來接受作為方法入參。 代碼很簡單,第一反應是不能將字元串的date屬性賦給d; 先嘗試 ...


  首先說明:以版本為Spring 4.3.0為測試對象; 開啟<mvc:annotation-driven />


  測試場景一:請求中含有date屬性,該類型為日期類型,SpringMvc採用@RequestParam來接受作為方法入參。

   代碼很簡單,第一反應是不能將字元串的date屬性賦給d;

    先嘗試輸入當前日期 2019-02-21 20:30 並提交,當然現在大多都是前端日期控制項來選擇日期並按照一定類型提交到後臺的;

    @RequestMapping(value="/form9")
    @ResponseBody
    public String form9(@RequestParam(name="date") Date d) {
        //基本類型會轉成包裝類型,嘗試轉換
        return "form9 Response Ok! " + d;
    }

    查看報錯信息: 沒能夠將字元串類型轉換需要的日期類型

Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.util.Date] for value '2019-02-21 20:30'; nested exception is java.lang.IllegalArgumentException
	at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:109)
	at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:36)
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
	at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:173)
	at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:108)
	at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64)
	at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:47)
	at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:688)

  

   其實,不是這樣的,當輸入日期為   2018/02/21 20:36:00 這樣的,你會發現又可以將字元串轉為日期類型

           image_thumb1_thumb

  

   原因我DEBUG簡單分析如下:因為@RequestParam註解決定了使用 RequestParamMethodArgumentResolver這個參數解析器,mvc-annotation註冊的參數類型轉換器 並沒有

  String—>Date類型的轉換器 , 但是用到了ObjectToObjectConvert這個轉換器;下圖貼一下其 類型轉換的convert 方法,就是嘗試去尋找 目標類 Date 構造方法

  即目標類構造方法需要唯一含有String類型的構造方法,然後實例化 該目標類,  沒找到就會拋出異常;

public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		if (source == null) {
			return null;
		}
		Class<?> sourceClass = sourceType.getType();
		Class<?> targetClass = targetType.getType();
		Member member = getValidatedMember(targetClass, sourceClass);

		try {
			if (member instanceof Method) {
				Method method = (Method) member;
				ReflectionUtils.makeAccessible(method);
				if (!Modifier.isStatic(method.getModifiers())) {
					return method.invoke(source);
				}
				else {
					return method.invoke(null, source);
				}
			}
			else if (member instanceof Constructor) {
				Constructor<?> ctor = (Constructor<?>) member;
				return ctor.newInstance(source);
			}
		}
		catch (InvocationTargetException ex) {
			throw new ConversionFailedException(sourceType, targetType, source, ex.getTargetException());
		}
		catch (Throwable ex) {
			throw new ConversionFailedException(sourceType, targetType, source, ex);
		}

		// If sourceClass is Number and targetClass is Integer, the following message should expand to:
		// No toInteger() method exists on java.lang.Number, and no static valueOf/of/from(java.lang.Number)
		// method or Integer(java.lang.Number) constructor exists on java.lang.Integer.
		throw new IllegalStateException(String.format("No to%3$s() method exists on %1$s, " +
				"and no static valueOf/of/from(%1$s) method or %3$s(%1$s) constructor exists on %2$s.",
				sourceClass.getName(), targetClass.getName(), targetClass.getSimpleName()));
	}

 

  因為Date有個雖然過時、但是確實是String類的構造方法:至於parse方法就是將String字元串轉為long類型,支持格式有常見的幾種:

2018/02/21 20:36:00  或者  2019/02/21 或者  02/21/2019 20:58:00

具體看是否支持你傳過來的日期格式,new Date(“your pattern”)是否拋出異常即可

    @Deprecated
    public Date(String s) {
        this(parse(s));
    }

  為了驗證我的說法,改動下一些地方:接收參數改成自定義MyDate對象,其中name屬性只是為了指定將名字為 date 的參數傳遞給MyDate的惟一的String構造方法;

@RequestMapping(value="/form9")
@ResponseBody
public String form9(@RequestParam(name="date") MyDate d) {
    //基本類型會轉成包裝類型,嘗試轉換
    return "form9 Response Ok! " + d;
}


public class MyDate {
    public MyDate(String str) throws ParseException {
        System.out.println("調用MyDate構造器");
        this.date = new SimpleDateFormat("yyyy-MM-dd").parse(str);
    }
    private Date date;

    @Override
    public String toString() {
        return "MyDate{" +"date=" + date +'}';
    }
}

 

   測試結果呢? 證明  SpringMvc mvc-annotation開啟,也是能夠將字元串類型用Date類型接收,只是接收方式就是調用Date的一個參數String類型的構造器轉換成Date類型;

image_thumb8_thumbimage_thumb6_thumb

 


測試場景二.@DateTimeFormat 接收自定義日期格式

說明:使用@DateTimeFormat,在<mvc:annotation-driven />基礎上,Spring版本4.3.0

有兩種方式:方式一,只需要當前項目編譯為JDK1.7、1.8以及更高版本,會自動支持@DateTimeFormat註解;

                    方式二,低版本的話需要引入 joda-time jar包;(看到網上有種說法需要引入該包才能支持@DateTimeFormat,覺得片面了,JDK1.7以及以上可以不添加該jar包);

  

  至於@DateTimeFormat用法有兩種:

   方式一:  結合@RequestParam

@RequestMapping(value="/form9")
    @ResponseBody
    public String form9(@RequestParam(name="date") @DateTimeFormat(pattern = "MM-yyyy-dd") Date d) {
        return "form9 Response Ok! " + d;
    }

 

  方式二: SpringMvc接收參數為自定義Java對象,在自定義的Java對象屬性上標註@DateTimeFormat註解;

(重要的是這種情況下Java對象必須要有空參的構造器,以及對應日期屬性的set方法,沒有構造器就拋出異常無法實例化Java對象,沒有set方法就是Java對象屬性為null)

@RequestMapping(value="/form8")
@ResponseBody
public String form8(TimePojo pojo) {
      return "form8 Response Ok! " + pojo;
}

@Setter
@Getter
@ToString
//節約篇幅,setter、getter、ToString來自lombok
public class TimePojo {
    @DateTimeFormat(pattern = "yyyy-mm-dd")
    private Date date;
}

 

 

 

下麵花一些篇幅記錄下,我Debug過程中分析的兩種方式的異同:

   方式一而言,參數解析器之前提過,RequestParamMethodArgumentResolver這個解析器來解析@RequestParam參數這點是不變的, 轉換器之前迫不得已找的ObjectToObjectConverter,

這次使用到的Converter,是AnnotationParserConverter,其專門針對String—>@DateTimeFormat的轉換器,  這些轉換器都註冊在SpringMvc的ConversionService中;1

  看下AnnotationParserConverter的convert方法,最後幾行調用了ParserConvert的convert方法,ParserConvert繼承自GenericConvert,其convert方法就是使用它的

parser對象的parse方法,當前情況也就是DateFormatter的parser方法, 下圖可以看到其parse方法等同於new SimpleDateFormat(“patten”).parse(“..”);

public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
  //當前情況是  獲取@DateTimeFormat註解
			Annotation ann = targetType.getAnnotation(this.annotationType);
			if (ann == null) {
				throw new IllegalStateException(
						"Expected [" + this.annotationType.getName() + "] to be present on " + targetType);
			}
			AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, targetType.getObjectType());
			GenericConverter converter = cachedParsers.get(converterKey);
			if (converter == null) {
				Parser<?> parser = this.annotationFormatterFactory.getParser(
						converterKey.getAnnotation(), converterKey.getFieldType());
                //當前情況annotationFormatterFactory為DateTimeFormatAnnotationFormatterFactory
  //獲取到的parser對象是 DateFormatter 
                      converter = new ParserConverter(this.fieldType, parser, FormattingConversionService.this);
				cachedParsers.put(converterKey, converter);
			}
			return converter.convert(source, sourceType, targetType);
		}

 

image

   到這裡也就完成了請求request中的String類型參數賦給方法Date類型入參,你以為到這裡就結束了,看下麵兩種情況也是同樣可以接受並轉換參數的!

image

 

補充說明:之前DateFormatter返回了Date類型參數,下圖是ParserConvert的convert方法,註意到paser完成以後, Date不符合需要參數Calendar類型要求,於是繼續調用conversionService的convert進行解析,正好SpringMvc <mvc:annotation-driven/>替我們註冊了Date->Calendar的轉換器,轉換的方法也非常簡單,

Calendar calendar = Calendar.getInstance();calendar.setTime(source); Long類型的轉換器也是類似的因為SpringMvc註冊的Date->Long的轉換器, date.getTime()即完成轉換;

image

 

方式二而言,參數解析器是ServletModelAttributeMethodProcessor,作用就是將請求參數映射到Java對象的屬性上;重要的一點,該Java對象需要有空參public類型構造器,不然無法實例化拋出異常,這種方式也支持級聯屬性設置,同樣的級聯的屬性也需要有空參構造方法; 看到過網上有個筆記 lombok的@Builder註解會使這種方式接受請求參數映射到屬性失效,因為生成的class文件破壞了預設構造器,就是沒有空的構造方法了;

 

 

扯得有點遠了,關於日期類型總結下幾點:

  不管SpringMvc是否預設支持將請求中字元串轉為Date類型,最輕鬆的方式應該就是使用@DateFormat註解,1.7以及更高的1.8版本直接就可以使用,其他還有方式比如自定義conversion-service


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

-Advertisement-
Play Games
更多相關文章
  • 前言 上一篇四種途徑提高RabbitMQ傳輸消息數據的可靠性(一)已經介紹了兩種方式提高數據可靠性傳輸的方法,本篇針對上一篇中提出的問題(1)與問題(2)提出解決常用的方法。 本文其實也就是結合以上四個方面進行講解的,主要參考《RabbitMQ實戰指南》(有需要PDF電子書的可以評論或者私信我),本 ...
  • 基礎數據類型 1、什麼是數據類型? 我們人類可以很容易的分清數字與字元的區別,但是電腦並不能呀,電腦雖然很強大,但從某種角度上看又很傻,除非你明確的告訴它,1是數字,“漢”是文字,否則它是分不清1和‘漢’的區別的,因此,在每個編程語言里都會有一個叫數據類型的東東,其實就是對常用的各種數據類型進行 ...
  • 為了防止服務消費鏈(多級服務之間的調用)上因某個服務出現故障,而導致級聯故障,進而造成整個系統不可用(簡稱為:雪崩效應),推出了熔斷、降級的處理方式:Hystrix斷路器(類似生活中電路保險絲)來解決這些潛在問題。 熔斷、降級是兩個概念,網上也有很多相關的說明,我這裡簡單通俗說明一下: 熔斷:當服務 ...
  • 在介紹了通用的序列操作後,我們來學習序列類型中的列表和元組 列表 回顧 我們已經初步學習了列表,在深入之前,讓我們簡單回顧一下以往的知識。 創建列表的方法: 給元素賦值: 刪除元素: 上一節我們還學習了分片、相加、乘法等通用序列操作,這裡就不過多闡述 分片賦值 在上一節中,我們介紹了通用的序列分片操 ...
  • 首先, 引入這節需要的 csv 文件 (已上傳) 輸出: 根據 'city'欄位分組: 輸出: 迴圈輸出分組後的數據: 輸出: 獲取其中的某一組數據: 輸出: 取每個組的最大值: 取每個組的平均值: 獲取每個組的常規信息: 輸出圖表: 以上, 就是關於 Pandas 分組的相關知識, enjoy~~ ...
  • 以下示例演示如何在 MATLAB® 中創建各種二維圖。 線圖 plot 函數用來創建由 x 和 y 值繪製而成的簡單線圖。 x = 0:0.05:5; y = sin(x.^2); figure plot(x,y) 線圖可顯示多組 x 和 y 數據。 y1 = sin(x.^2); y2 = cos ...
  • 表達式中運算數據類型不一致怎麼辦? 參數傳遞:就是調用方法的時候,向方法內傳入數據的動作。 形式參數:在定義方法的時候,寫在小括弧之內的參數。(被動接收數據的) eg:public static int sum(int a,int b)//這裡的a和b,是在定義的時候寫的,所以是形式參數即形參。 實 ...
  • 79、字元串排序。 80、海灘上有一堆桃子,五隻猴子來分。第一隻猴子把這堆桃子平均分為五份,多了一個,這隻猴子把多的一個扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了一個,它同樣把多的一個扔入海中,拿走了一份,第三、第四、第五隻猴子都是這樣做的,問海灘上原來最少有多少個桃子? 8 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...