Controller方法的参数类型可以是基本类型,也可以是封装后的普通Java类型。若这个普通Java类型没有声明任何注解,则意味着它的每一个属性都需要到Request中去查找对应的请求参数。众所周知,无论客户端传入的是什么类型的请求参数,最终都要以字节的形式传给服务端。而服务端通过Request的getParameter方法取到的参数也都是字符串形式的结果。所以,需要有一个把字符串形式的参数转换成服务端真正需要的类型的转换工具,在spring中这个转换工具为WebDataBinder。
 
   WebDataBinder不需要我们自己去创建,我们只需要向它注册参数类型对应的属性编辑器PropertyEditor。PropertyEditor可以将字符串转换成其真正的数据类型,它的void setAsText(String text)方法实现数据转换的过程。
 
  具体的做法是,在Controller中声明一个InitBinder方法,方法中利用WebDataBinder将自己实现的或者spring自带的PropertyEditor进行注册。像下面这样:

  1. @InitBinder
  2. public void initBinder(WebDataBinder binder) throws Exception {
  3. binder.registerCustomEditor(Long.class, new CustomNumberEditor(Long.class, true));
  4. binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
  5. }
  
  处理没有任何注解的普通Java类型的参数解析器是ModelAttributeMethodProcessor,下面是参加解析方法的代码:
  1. public final Object resolveArgument(
  2. MethodParameter parameter, ModelAndViewContainer mavContainer,
  3. NativeWebRequest request, WebDataBinderFactory binderFactory)
  4. throws Exception {
  5. String name = ModelFactory.getNameForParameter(parameter);
  6. Object target = (mavContainer.containsAttribute(name)) ?
  7. mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
  8. WebDataBinder binder = binderFactory.createBinder(request, target, name);
  9. if (binder.getTarget() != null) {
  10. bindRequestParameters(binder, request);
  11. validateIfApplicable(binder, parameter);
  12. if (binder.getBindingResult().hasErrors()) {
  13. if (isBindExceptionRequired(binder, parameter)) {
  14. throw new BindException(binder.getBindingResult());
  15. }
  16. }
  17. }
  18. mavContainer.addAllAttributes(binder.getBindingResult().getModel());
  19. return binder.getTarget();
  20. }

每次请求到来后的参数解析都会利用WebDataBinderFactory创建一个binder对象,然后从这个binder中取得最终解析好的参数对象。WebDataBinderFactory是在InvocableHandlerMethod中定义的,即不同的Controller方法有着不同的WebDataBinderFactory。其实创建binder的同时还对binder进行了初始化,这个初始化过程就会执行Controller中的InitBinder方法。InitBinderDataBinderFactory实现了初始化binder的方法:

  1. public void initBinder(WebDataBinder binder, NativeWebRequest request) throws Exception {
  2. for (InvocableHandlerMethod binderMebinderMethod thod : this.binderMethods) {
  3. if (isBinderMethodApplicable(binderMethod, binder)) {
  4. Object returnValue = binderMethod.invokeForRequest(request, null, binder);
  5. if (returnValue != null) {
  6. throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);
  7. }
  8. }
  9. }
  10. }
      上面方法中的binderMethods就是在Controller中定义的InitBinder方法,并且binderMethod 同Controller中的其他方法一样也是InvocableHandlerMethod。从上面的代码可以看出,InitBinder方法可以声明多个,WebDataBinderFactory初始化binder的时候会分别调用每个InitBinder方法。而我们在初始化的过程中使用了binder.registerCustomEditor,间接地向BeanWrapperImpl中注册了传入的PropertyEditor,以便在参数类型转换的时候使用。
 
      还记得刚才的ModelAttributeMethodProcessor解析参数时,创建binder之后调用了bindRequestParameters实现了请求参数的绑定,它的子类ServletModelAttributeMethodProcessor重写了这个方法:
  1. protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
  2. ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
  3. ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
  4. servletBinder.bind(servletRequest);
  5. }
    
     不论是父类还是子类,其实都是调用了binder的bind方法。下面是ServletRequestDataBinder的bind方法
  1. public void bind(ServletRequest request) {
  2. MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
  3. MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
  4. if (multipartRequest != null) {
  5. bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
  6. }
  7. addBindValues(mpvs, request);
  8. doBind(mpvs);
  9. }
 

这个方法跟依赖注入的过程非常相似,依赖注入是根据属性在容器中找到满足条件的对象,然后设置到当前的bean中。而上面的方法不是在容器中查找,而是从Request中获取,即把Request中的请求参数注入到binder的target中去。此时进行类型转换的就是刚刚注册的PropertyEditor,因为InitBinder方法每次都会执行,所以使用者可以在每个Controller中对相同类型的参数定义不同的参数转换方式。

经过了bindRequestParameters方法的处理,现在binder中target(即HandlerMethod的参数)已经包含了Request中的请求参数。

 
      那么,现在还有一个问题,InvocableHandlerMethod中的WebDataBinderFactory是如何来的呢?它的创建过程在RequestMappingHandlerAdapter(本文所有逻辑过程均假定使用RequestMappingHandlerAdapter):
 
  1. private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
  2. Class<?> handlerType = handlerMethod.getBeanType();
  3. Set<Method> methods = this.dataBinderFactoryCache.get(handlerType);
  4. if (methods == null) {
  5. methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
  6. this.dataBinderFactoryCache.put(handlerType, methods);
  7. }
  8. List<InvocableHandlerMethod> binderMethods = new ArrayList<InvocableHandlerMethod>();
  9. for (Method method : methods) {
  10. InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
  11. binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
  12. binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
  13. binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
  14. binderMethods.add(binderMethod);
  15. }
  16. return createDataBinderFactory(binderMethods);
  17. }

SpringMVC中WebDataBinder的应用及原理的更多相关文章

  1. 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]

    目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口介绍 HandlerMethodArgumen ...

  2. 详解SpringMVC中Controller的方法中参数的工作原理

    Spring MVC中Controller的处理方法的参数可以是Integer,String,自定义对象,ServletRequest,ServletResponse,ModelAndView等等,非 ...

  3. 详解SpringMVC中Controller的方法中参数的工作原理——基于maven

    转自:http://www.tuicool.com/articles/F7byQn 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...

  4. 【MVC - 参数原理】详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]

    前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/spring ...

  5. SpringMVC中Controller

    详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析] 目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodR ...

  6. SpringMVC中注解@RequestBody和@ResponseBody的使用区别

    首先上源码 在面试时经常会问到我们如何使用SpringMVC将Http请求转换为java对象,或者又是问如何将结果转换为java的呢? SpringMVC在接收到请求之后HandlerMapping像 ...

  7. 8.springMVC中的RESTful架构风格

    RESTful架构:是一种设计的风格,并不是标准,只是提供了一组设计原则和约束条件,也是目前比较流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便,所以正得到越来越多网站的采用. 关于 ...

  8. JavaEE开发之SpringMVC中的静态资源映射及服务器推送技术

    在上篇博客中,我们聊了<JavaEE开发之SpringMVC中的自定义拦截器及异常处理>.本篇博客我们继续的来聊SpringMVC的东西,下方我们将会聊到js.css这些静态文件的加载配置 ...

  9. 将SpringMVC中的HttpMessageConverter替换为Gson

    读者们看到这个标题也许会感到奇怪,SpringMVC中默认的HttpMessageConverter不是Jackson吗,但是我在使用的过程中发现Jackson并不好用,如果有一些复杂的嵌套类型,当然 ...

随机推荐

  1. html5.js让IE(包含IE6)支持HTML5元素方法

    原文地址:http://blog.sina.com.cn/s/blog_62a36ec401018oqb.html html5.js让IE(包含IE6)支持HTML5元素方法 微软的最新浏览器IE8及 ...

  2. Hazelcast 内存数据网格

    Hazelcast ( www.hazelcast.com)是一种内存数据网格 in-memory data grid,提供Java程序员关键任务交易和万亿级内存应用. Hazelcast的集群属于“ ...

  3. SQL2000查看表的大小

    SQL2000查看表的大小 标签: sqlsql server数据库报表tableinsert 2011-06-08 11:47 4013人阅读 评论(0) 收藏 举报 SQL2000查看表的大小 本 ...

  4. Linux环境安装配置maven

     按照下面命令执行即可 1.下载apache-maven-3.5.3-bin.tar.gz 并上传到服务器上 提取地址:https://pan.baidu.com/s/11nxZp84lmonRBCR ...

  5. VS2017 下使用 git. git服务器使用gitblit

    注意事项: 创建的GIT不要包括有中文, 不然会不成功的..... gitblit安装及配置及客户端下载略过...... 可参考:  https://www.cnblogs.com/jeremylee ...

  6. Unity,android和IOS 防止八门神器注入

    八门神器主要是不断筛选,来获取关键属性(比如金币)在内存中的地址,再根据该地址来修改指向的数据就可以成功. 因此,我们需要在金币读取和设置的时候,使用一个偏移量,来达到干扰的目的就可以了 未经仔细测试 ...

  7. Linux网络性能评估工具iperf 、CHARIOT测试网络吞吐量

    网络性能评估主要是监测网络带宽的使用率,将网络带宽利用最大化是保证网络性能的基础,但是由于网络设计不合理.网络存在安全漏洞等原因,都会导致网络带宽利用率不高.要找到网络带宽利用率不高的原因,就需要对网 ...

  8. 【leetcode刷题笔记】Majority Element

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  9. Django学习笔记之Django中间件

    准备 我们在前面的课程中已经学会了给视图函数加装饰器来判断是用户是否登录,把没有登录的用户请求跳转到登录页面.我们通过给几个特定视图函数加装饰器实现了这个需求.但是以后添加的视图函数可能也需要加上装饰 ...

  10. 在Windows Server 2008 R2上打开ping的方法

    默认安装完Windows Server 2008 R2后,从外面ping服务器的地址是ping不通的,原因是服务器防火墙默认关闭了ICMP的回显请求.需要按照如下方法打开: 在服务器管理器中选择“配置 ...