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类,该类允许在目标对象上设置属性值,支持数据验证和绑定,实现了PropertyEditorRegistry和TypeConverter

先打开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和自定义的@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

5.总结

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解析参数

6.参考

https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conversion

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

文中难免有不足,还望指出

年三十晚上完成了这篇文章,新年快乐

原文地址:https://www.cnblogs.com/Java-Starter/p/10352276.html

时间: 2024-10-14 10:39:18

SpringMVC源码阅读:属性编辑器、数据绑定的相关文章

SpringMVC源码阅读:拦截器

1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC拦截器的工作原理 2.源码分析 进入SpringMVC核心类DispatcherServlet的doDispatch方法,在SpringMVC源码阅读:核心分发器DispatcherServlet曾经分析过,这里再分析一遍 936行获得HandlerExec

SpringMVC源码阅读:视图解析器

1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC如何完成视图解析的 2.源码分析 在SpringMVC源码阅读:拦截器分析过doDispatch的运行过程,这里再分析一遍 回到DispatcherServlet类的doDispatch方法,看看doDispatch如何获取ModelAndView Hand

SpringMVC源码阅读:异常解析器

1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC如何完成异常解析.捕捉异常,并自定义异常和异常解析器 2.源码分析 进入DispatcherServlet的processDispatchResult方法 1024行判断异常是否是ModelAndViewDefiningException类型,如果是,直接返

SpringMVC源码阅读(一)

DispatcherServlet是整个SPringMVC初始化和处理请求的重要类,作为一个servlet,用友init  service destroy 方法 <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 可以自定义s

Spring Framework及SpringMVC源码阅读1

web应用的启动过程 熟悉的web应用都带有一个web.xml配置文件,web应用的启动就是从这个web.xml开始的.web应用放入servlet容器如tomcat中,tomcat启动时加载web.xml.web.xml中有四个常用的配置项: listener 经常被叫做监听器 context-param filter 经常被叫做过滤器 servlet servlet将接收到的http请求交给对应的servlet处理 启动过程时这样的: 首先创建web.xml中配置的监听器,监听容器中发生的事

SpringMVC源码阅读(二)

今天分析下ViewResolver和View的实现  下面是ModelAndView的实现 package org.springframework.web.servlet; import java.util.Map; import org.springframework.ui.ModelMap; import org.springframework.util.CollectionUtils; public class ModelAndView { /** View instance or vie

SpringMVC源码阅读(三)

先理一下Bean的初始化路线 org.springframework.beans.factory.support.AbstractBeanDefinitionReader public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int c

SpringMVC源码阅读HandlerAdapter-RequestMappingHandlerAdapter(七)

接口 public interface HandlerAdapter { /** * 是否能处理指定Handler * @param var1 * @return */ boolean supports(Object var1); /** * 处理Handler * @param var1 * @param var2 * @param var3 * @return * @throws Exception */ @Nullable ModelAndView handle(HttpServletRe

SpringMVC源码阅读ViewResolver如何处理ContentNegotiatingViewResolver(九)

类图 原文地址:https://www.cnblogs.com/LQBlog/p/12228515.html