SpringMVC是目前J2EE平臺的主流Web框架,不熟悉的園友能夠看SpringMVC源碼閱讀入門,它交代了SpringMVC的基礎知識和源碼閱讀的技巧html
本文將經過源碼(基於Spring4.3.7)分析,弄清楚SpringMVC如何經過類型轉換完成數據綁定和屬性編輯器的原理,並自定義屬性編輯器java
進入RequestMappingHandlerAdapter,該類支持參數解析和數據返回,進入invokeHandlerMethod方法git
794行構造WebDataBinderFactory,傳入HandlerMethod參數github
點進去getDataBinderFactory方法,看看它作什麼spring
886行獲取@InitBinder方法json
891行查找帶有@ControllerAdvice註解支持的Controllerapi
看下RequestParamMethodArgumentResolver的父類AbstractNamedValueMethodArgumentResolver的resolveArgument方法瀏覽器
117行獲取到@InitBinder註解修飾的方法和@ControllerAdvice中的@InitBinder註解修飾的方法mvc
118行建立一個ExtendedServletRequestDataBinderapp
120行arg獲取參數轉換結果
binderFactory變量是WebDataBinderFactory類型,打開WebDataBinderFactory,該類在Spring3.1引入,用來建立WebDataBinder
進入WebDataBinder,該類用於處理Web請求參數和JavaBean之間的數據綁定,ctrl+alt+h打開類繼承圖,WebDataBinder繼承DataBinder
打開DataBinder類,該類容許在目標對象上設置屬性值,支持數據驗證和綁定,實現了PropertyEditorRegistry和TypeConverter
先打開PropertyEditorRegistry,該類給註冊的JavaBean封裝方法,註釋提到被BeanWrapper繼承,由BeanWrapperImpl實現
BeanWrappert接口提供操做JavaBean的方法,配置set/get方法
再打開TypeConverter,該類是定義類型轉換方法的接口,和PropertyEditorRegistry組合使用
最後咱們找到PropertyEditor,它是屬性編輯的核心接口,看它的子類
稍後咱們自定義屬性編輯器要繼承該類,重寫setAsText方法
建立實體類TestModel
public class TestModel { private int age; private Date birth; private String name; private boolean good; private long times; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isGood() { return good; } public void setGood(boolean good) { this.good = good; } public long getTimes() { return times; } public void setTimes(long times) { this.times = times; } }
測試方法
@RequestMapping(value = "/testWrapper", produces={"application/json; charset=UTF-8"}) @ResponseBody public TestModel testWrapper() { TestModel tm = new TestModel(); BeanWrapper bw = new BeanWrapperImpl(tm); bw.setPropertyValue("good", "1"); return tm; }
瀏覽器輸入http://localhost:8080/springmvcdemo/test/testWrapper
在PropertyEditorSupport(實現PropertyEditor)的子類CustomBooleanEditor中,setAsText方法對上述現象進行了處理
@RequestMapping(value = "/testNotUseWrapper", produces={"application/json; charset=UTF-8"}) @ResponseBody public TestModel testNotUseWrapper() { TestModel tm = new TestModel(); BeanWrapperImpl bw = new BeanWrapperImpl(false); bw.setWrappedInstance(tm); bw.setPropertyValue("good", "1"); return tm; }
瀏覽器輸入http://localhost:8080/springmvcdemo/test/testNotUseWrapper
由於沒有對應的屬性編輯器,致使String類型「1」沒法轉換成Boolean類型
在SpringMVC源碼閱讀:Controller中參數解析我說過,ServletModelAttributeMethodProcessor處理無註解對象
@RequestMapping(value = "testObj", produces={"application/json; charset=UTF-8"}) @ResponseBody public Map testObj(Employee e) { Map resultMap = new HashMap(); resultMap.put("Employee",e); return resultMap; }
瀏覽器輸入http://localhost:8080/springmvcdemo/test/testObj?id=1&name=s&age=12&dept.id=1&dept.name=20
resolveArgument方法在ServletModelAttributeMethodProcessor已廢棄,在其父類ModelAttributeMethodProcessor被實現
99行獲取參數別名
100行獲取屬性列表
110行建立ExtendedServletRequestDataBinder,前文已經說過
113行綁定請求參數,此時屬性列表參數綁定完畢
自定義屬性編輯器,實現PropertyEditorSupport
public class CustomDeptEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { if(text.indexOf(",") > 0) { Dept dept = new Dept(); String[] arr = text.split(","); dept.setId(Integer.parseInt(arr[0])); dept.setName(arr[1]); setValue(dept); } else { throw new IllegalArgumentException("dept param is error"); } } }
在TestController添加@InitBinder
@InitBinder public void initBinderDept(WebDataBinder binder) { binder.registerCustomEditor(Dept.class, new CustomDeptEditor()); }
添加@ControllerAdvice,保證InitBinder應用到RequestMapping,就是說Controller裏定義的@InitBinder和自定義的@ControllerAdvice裏@InitBinder存在一個便可
@ControllerAdvice public class InitBinderControllerAdvice { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Dept.class, new CustomDeptEditor()); } }
dispatcher-servlet須要配置component-scan,掃描到咱們定義的ControllerAdvice
<context:component-scan base-package="org.format.demo.controlleradvice" />
瀏覽器輸入http://localhost:8080/springmvcdemo/test/testObj?id=1&name=s&age=12&dept=1,research
PropertyEditor是屬性編輯器的接口,setAsText是核心方法,實現類PropertyEditorSupport
PropertyEditorRegistry接口給JavaBean註冊對應的屬性編輯器,實現類PropertyEditorRegistrySupport的createDefaultEditors建立默認的屬性編輯器
TypeConverter接口,經過該接口,能夠將value轉換爲指定類型對象,實現類TypeConverterSupport將類型轉換委託給TypeConverterDelegate處理
BeanWrapper接口操做JavaBean,配置set/get方法和查詢數據的可讀可寫性,實現類爲BeanWrapperImpl
DataBinder用來set值和數據驗證,WebDataBinder處理對Web請求參數到JavaBean的數據綁定
RequestMappingHandlerAdapter調用invokeHandlerMethod方法建立WebDataBinderFactory,WebDataBinderFactory建立WebDataBinder
最後HandlerMethodArgumentResolver解析參數
https://docs.spring.io/spring/docs/current/javadoc-api/
http://www.cnblogs.com/fangjian0423/p/springMVC-databind-typeconvert.html
https://github.com/spring-projects/spring-framework
文中不免有不足,還望指出
年三十晚上完成了這篇文章,新年快樂