SpringMVC源码阅读:属性编辑器、数据绑定

1.前言SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC如何通过类型转换完成数据绑定和属性编辑器的原理,并自定义属性编辑器2.源码分析进入RequestMappingHandlerAdapter,该类支持参数解析和数据...

SpringMVC源码阅读:属性编辑器、数据绑定

1.前言

SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧

本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC如何通过类型转换完成数据绑定和属性编辑器的原理,并自定义属性编辑器

2.源码分析

进入RequestMappingHandlerAdapter,该类支持参数解析和数据返回,进入invokeHandlerMethod方法

794行构造WebDataBinderFactory,传入HandlerMethod参数

点进去getDataBinderFactory方法,看看它做什么

886行获取@InitBinder方法

891行查找带有@ControllerAdvice注解支持的Controller

看下RequestParamMethodArgumentResolver的父类AbstractNamedValueMethodArgumentResolver的resolveArgument方法

117行获取到@InitBinder注解修饰的方法和@ControllerAdvice中的@InitBinder注解修饰的方法

118行创建一个ExtendedServletRequestDataBinder

120行arg获取参数转换结果

binderFactory变量是WebDataBinderFactory类型,打开WebDataBinderFactory,该类在Spring3.1引入,用来创建WebDataBinder

进入WebDataBinder,该类用于处理Web请求参数和JavaBean之间的数据绑定,ctrl alt h打开类继承图,WebDataBinder继承DataBinder

打开DataBinder类,该类允许在目标对象上设置属性值,支持数据验证和绑定,实现了PropertyEditorRegistryTypeConverter

先打开PropertyEditorRegistry,该类给注册的JavaBean封装方法,注释提到被BeanWrapper继承,由BeanWrapperImpl实现

BeanWrappert接口提供操作JavaBean的方法,配置set/get方法

再打开TypeConverter,该类是定义类型转换方法的接口,和PropertyEditorRegistry组合使用

最后我们找到PropertyEditor,它是属性编辑的核心接口,看它的子类

稍后我们自定义属性编辑器要继承该类,重写setAsText方法

3.实例

3.1 测试BeanWrapper

创建实体类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方法对上述现象进行了处理

3.2 测试不使用BeanWrapper

 @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类型

3.3 测试无注解对象参数绑定

在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行绑定请求参数,此时属性列表参数绑定完毕

4.编写自定义属性编辑器

自定义属性编辑器,实现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和自定义的@C

源文地址:https://www.guoxiongfei.cn/cntech/9672.html