源碼解析(1)

SpringMVC中WebDataBinder的應用及原理

Controller方法的參數類型可以是基本類型,也可以是封裝后的普通Java類型。若這個普通Java類型沒有聲明任何注解,則意味著它的每一個屬性都需要到Request中去查找對應的請求參數。眾所周知,無論客戶端傳入的是什么類型的請求參數,最終都要以字節的形式傳給服務端。而服務端通過Request的getParameter方法取到的參數也都是字符串形式的結果。所以,需要有一個把字符串形式的參數轉換成服務端真正需要的類型的轉換工具,在spring中這個轉換工具為WebDataBinder。

WebDataBinder不需要我們自己去創建,我們只需要向它注冊參數類型對應的屬性編輯器PropertyEditor。PropertyEditor可以將字符串轉換成其真正的數據類型,它的void setAsText(String text)方法實現數據轉換的過程。

具體的做法是,在Controller中聲明一個InitBinder方法,方法中利用WebDataBinder將自己實現的或者spring自帶的PropertyEditor進行注冊。像下面這樣:

@InitBinderpublicvoidinitBinder(WebDataBinder binder)throwsException{? ? ? binder.registerCustomEditor(Long.class,newCustomNumberEditor(Long.class,true));? ? ? binder.registerCustomEditor(Date.class,newCustomDateEditor(newSimpleDateFormat("yyyy-MM-dd"),true));? }? ? ```? 處理沒有任何注解的普通Java類型的參數解析器是ModelAttributeMethodProcessor,下面是參加解析方法的代碼:```javapublicfinalObjectresolveArgument(

MethodParameter parameter, ModelAndViewContainer mavContainer,

NativeWebRequest request, WebDataBinderFactory binderFactory)throwsException{? ? ? ? String name = ModelFactory.getNameForParameter(parameter);? ? ? Object target = (mavContainer.containsAttribute(name)) ?? ? ? ? ? ? ? mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);? ? ? ? WebDataBinder binder = binderFactory.createBinder(request, target, name);if(binder.getTarget() !=null) {? ? ? ? ? bindRequestParameters(binder, request);? ? ? ? ? validateIfApplicable(binder, parameter);if(binder.getBindingResult().hasErrors()) {if(isBindExceptionRequired(binder, parameter)) {thrownewBindException(binder.getBindingResult());? ? ? ? ? ? ? }? ? ? ? ? }? ? ? }? ? ? ? mavContainer.addAllAttributes(binder.getBindingResult().getModel());returnbinder.getTarget();? }

每次請求到來后的參數解析都會利用WebDataBinderFactory創建一個binder對象,然后從這個binder中取得最終解析好的參數對象。WebDataBinderFactory是在InvocableHandlerMethod中定義的,即不同的Controller方法有著不同的WebDataBinderFactory。其實創建binder的同時還對binder進行了初始化,這個初始化過程就會執行Controller中的InitBinder方法。InitBinderDataBinderFactory實現了初始化binder的方法:

java

public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {

for (InvocableHandlerMethod binderMebinderMethod thod : this.binderMethods) {

if (isBinderMethodApplicable(binderMethod, binder)) {

Object returnValue = binderMethod.invokeForRequest(request, null, binder);

if (returnValue != null) {

throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);

}

}

}

}

上面方法中的binderMethods就是在Controller中定義的InitBinder方法,并且binderMethod 同Controller中的其他方法一樣也是InvocableHandlerMethod。從上面的代碼可以看出,InitBinder方法可以聲明多個,WebDataBinderFactory初始化binder的時候會分別調用每個InitBinder方法。而我們在初始化的過程中使用了binder.registerCustomEditor,間接地向BeanWrapperImpl中注冊了傳入的PropertyEditor,以便在參數類型轉換的時候使用。

還記得剛才的ModelAttributeMethodProcessor解析參數時,創建binder之后調用了bindRequestParameters實現了請求參數的綁定,它的子類ServletModelAttributeMethodProcessor重寫了這個方法:

protectedvoidbindRequestParameters(WebDataBinder binder, NativeWebRequest request){? ? ? ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);? ? ? ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;? ? ? servletBinder.bind(servletRequest);? }

不論是父類還是子類,其實都是調用了binder的bind方法。下面是ServletRequestDataBinder的bind方法

publicvoidbind(ServletRequest request){? ? ? MutablePropertyValues mpvs =newServletRequestParameterPropertyValues(request);? ? ? MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);if(multipartRequest !=null) {? ? ? ? ? bindMultipart(multipartRequest.getMultiFileMap(), mpvs);? ? ? }? ? ? addBindValues(mpvs, request);? ? ? doBind(mpvs);? }

這個方法跟依賴注入的過程非常相似,依賴注入是根據屬性在容器中找到滿足條件的對象,然后設置到當前的bean中。而上面的方法不是在容器中查找,而是從Request中獲取,即把Request中的請求參數注入到binder的target中去。此時進行類型轉換的就是剛剛注冊的PropertyEditor,因為InitBinder方法每次都會執行,所以使用者可以在每個Controller中對相同類型的參數定義不同的參數轉換方式。經過了bindRequestParameters方法的處理,現在binder中target(即HandlerMethod的參數)已經包含了Request中的請求參數。? 那么,現在還有一個問題,InvocableHandlerMethod中的WebDataBinderFactory是如何來的呢?它的創建過程在RequestMappingHandlerAdapter(本文所有邏輯過程均假定使用RequestMappingHandlerAdapter):

privateWebDataBinderFactorygetDataBinderFactory(HandlerMethod handlerMethod)throwsException{? ? ? Class handlerType = handlerMethod.getBeanType();? ? ? Set methods =this.dataBinderFactoryCache.get(handlerType);if(methods ==null) {? ? ? ? ? methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);this.dataBinderFactoryCache.put(handlerType, methods);? ? ? }? ? ? List binderMethods =newArrayList();for(Method method : methods) {? ? ? ? ? InvocableHandlerMethod binderMethod =newInvocableHandlerMethod(handlerMethod.getBean(), method);? ? ? ? ? binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);? ? ? ? ? binderMethod.setDataBinderFactory(newDefaultDataBinderFactory(this.webBindingInitializer));? ? ? ? ? binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);? ? ? ? ? binderMethods.add(binderMethod);? ? ? }returncreateDataBinderFactory(binderMethods);? }

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容