zoukankan      html  css  js  c++  java
  • 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和自定义的@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

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

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

  • 相关阅读:
    python3中的文件操作
    python3网络爬虫实现有道词典翻译功能
    C++11 thread condition_variable mutex 综合使用
    goland scope pattern 设置
    Go 1.11 Module 介绍
    hustOJ 添加 golang 支持
    docker 搭建 hustoj
    最长重复字符串题解 golang
    golang 结构体中的匿名接口
    使用aliyun cli工具快速创建云主机
  • 原文地址:https://www.cnblogs.com/Java-Starter/p/10352276.html
Copyright © 2011-2022 走看看