第九章 数据绑定流程分析
数据转换
springMVC封装自定义类型对象的时候;
javaBean要和页面提交的数据进行一一绑定
- 页面提交的所有数据都是字符串
- Integer age ,Date birth;age=18&gender=1...
- String age = request.getParameter("age")
牵扯到一下操作:
- 数据绑定期间的数据类型转换?String--Integer String--Boolean
- 数据绑定期间的数据格式化问题
birth = 2017-01-10 -> Date 2017/01/10..... - 数据校验
我们提交的数据必须是合法的?前端校验:js+正则表达式、后端校验:复杂数据 【校验成功、校验失败】
WebDataBinder:数据绑定器负责数据绑定工作
数据绑定期间产生的类型转换、格式化、数据校验等问题
bindingResult:处理异常
conversionService:类型转换 格式化
validators:数据校验器
Spring MVC 上下文中内建了很多转换器,可完成大多数 Java 类型的转换工作ConversionService converters = java.lang.Boolean->java.lang.String: org.springframework.core.convert.support.ObjectToStringConverter@f874ca java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9 java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961 java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5 java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8 java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626 java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800 java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12 java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1 java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828 java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23 java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f ……
POJO 请求域?session?创建一个?绑定部分的代码
ModelAttributeMethodProcessor:新源码 mvc相关注解的影响
数据绑定流程原理
- Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
- DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
- 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象
- Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是 DataBinder,运行机制如下:
自定义类型转换器
类型转换器概述
- ConversionService 是 Spring 类型转换体系的核心接口。
- 可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个
ConversionService. Spring 将自动识别出 IOC 容器中的 ConversionService,并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换 - 可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器
ConversionService组件:负责数据类型的转换以及格式化功能;
ConversionService中有非常多的converter
不同类型的转换和格式化用他自己的converter
Spring 支持的转换器类型
Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactoryBean 中:
- Converter<S,T>:将 S 类型对象转为 T 类型对象 √
- ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类 x
- GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换 x
自定义转换器示例
需求:字符串转换为对象: 12-java编程思想=>book
如果没有加自定义转换器组件-->报错
《% pageContext.setAttribute("ctp", request.getContextPath()); %> 《form action="${ctp}/book" method="post"> 《!-- 直接封装成book对象 --> 《input type="text" name="bookInfo" value="12-java编程思想"> 《input type="submit" value="提交"/> 《/form>
步骤1
conversionService:是一个接口,里有Converter(转换器)进行工作
步骤2
实现converter接口,写一个自定义的类型转换器
import org.springframework.core.convert.converter.Converter; import com.project.bean.Book; public class MyConverterFunc implements Converter<String, Book> { public Book convert(String s) { Book book = new Book(); if(s.contains("-")) { String arr[] = s.split("-"); book.setId(arr[0]); book.setBookName(arr[1]); } return book; } }
步骤3
Converter是ConversionService中的组件,你的Converter得放进Converter中
你的Converter得放进ConversionService中
将WebDataBinder中的ConversionService设置成我们这个加了自定义类型转换器的ConversionService
配置出ConversionService
<!-- 告诉SpringMVC 别用默认的ConversionService,而用我自定义的ConversionService 可能有我们自定义的Converter --> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.project.component.MyConverterFunc"/> </set> </property> </bean> <mvc:default-servlet-handler/> <!-- 可以告诉springmvc使用我们自己配置的类型转换组件 --> <mvc:annotation-driven conversion-service="conversionService"/>
效果 springmvc 会自动找到String->Book 的转换器
总结:
- 实现Converter接口,做一个自定义类型的转换器
- 将这个converter配置在ConversionService中
- 告诉springmvc使用这个ConversionService
数据的格式化
数据格式化概述
- 对属性对象的输入/输出进行格式化,从其本质上讲依然属于 “类型转换” 的范畴。
- Spring 在格式化模块中定义了一个实现 ConversionService 接口的
FormattingConversionService 实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能 - FormattingConversionService 拥有一个 FormattingConversionServiceFactroyBean 工厂类,后者用于在 Spring 上下文中构造前者,FormattingConversionServiceFactroyBean 内部已经注册了 :
- NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用
@NumberFormat 注解 - JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用
@DateTimeFormat 注解 - 装配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动了。
- 《mvc:annotation-driven/> 默认创建的 ConversionService 实例即为
DefaultFormattingConversionService
DeBug 看看替换ConversionService是否也替换了其他转换器
- 源码上WebDataBinder上的ConversionService组件就替换了 默认的转换器都还 在 注册的新添上
- 默认的转换器
《mvc:annotation-driven/>配置在什么时候必须配置
- 直接配置响应的页面:无需经过控制器来执行结果 ;但会导致其他请求路径失效,需要配置mvc:annotation-driven标签
<mvc:view-controller path="/success" view-name="success"/>
- RESTful-CRUD操作,删除时,通过jQuery执行delete请求时,找不到静态资源,需要配置mvc:annotation-driven标签
<mvc:default-servlet-handler/> 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。
- 配置类型转换器服务时,需要指定转换器服务引用
《mvc:annotation-driven conversion-service=“conversionService”/> 会将自定义的ConversionService 注册到 Spring MVC 的上下文中 - 后面完成JSR 303数据验证,也需要配置
关于 《mvc:annotation-driven /> 作用
《mvc:annotation-driven /> 会自动注册:
RequestMappingHandlerMapping 、RequestMappingHandlerAdapter 与
ExceptionHandlerExceptionResolver 三个bean。
还将提供以下支持:
支持使用 ConversionService 实例对表单参数进行类型转换
支持使用 @NumberFormat、@DateTimeFormat 注解完成数据类型的格式化
支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
支持使用 @RequestBody 和 @ResponseBody 注解
结合源码分析(在bean对象的set方法上设置断点进行调试)
既没有配置 《mvc:default-servlet-handler/> 也没有配置 《mvc:annotation-driven/> 【动态资源能访问 静态不行.html、.js、.img】
handlerAdapters:方法执行适配器 对我们有用是第三个 mv = ha.handle(...)
都没有配置情况下,AnnotationMethodHandlerAdapter是默认出厂设置,干活的(过期)。
HandlerMapping:第二个Default...类中的HashMap 保存了每一个资源的映射信息;却没有保存静态资源映射请求.js .html ....
另外:conversionService是null(类型转换器是不起作用的)静态资源不起作用 四月 30, 2016 3:52:21 下午 org.springframework.web.servlet.PageNotFound noHandlerFound 警告: No mapping found for HTTP request with URI [/SpringMVC_03_RESTFul_CRUD/scripts/jquery-1.9.1.min.js] in DispatcherServlet with name 'springDispatcherServlet'
配置了 《mvc:default-servlet-handler/> 但没有配置 《mvc:annotation-driven/> 【静态资源能访问 动态资源不能访问】
AnnotationMethodHandlerAdapter被取消,解决了静态资源查找,但是@RequestMapping不好使了。
handlerAdapters:【处理注解的适配器都没有了】
HandlerMapping:[SimpleUrl...就是把所有请求都交给Tomcat来处理,tomcat只能处理静态资源]既配置了 《mvc:default-servlet-handler/> 又配置 《mvc:annotation-driven/>【重要 都要能访问】
AnnotationMethodHandlerAdapter被替换成RequestMappingHandlerAdapter来干活了。
如果没有配置《mvc:annotation-driven/>标签时,conversionService为null.
handlerAdapters:【Annotation被换成requestMapping】
HandlerMapping:【requestMapping....中有handlerMethods所有详细信息 如每一个请求用哪个方法来处理 动态资源可以处理 里面找不到取别的地方找 所以静态资源也可以】AnnotationMethodHandlerAdapter已经过时,Spring3.2推荐RequestMappingHandlerAdapter来替代。所以说,默认情况下,没有配置这两个配置时,HelloWorld 程序可以正常运行,但是,涉及到静态资源查找的时候,就必须配置这个《mvc:annotation-driven/>配置了
BeanDefinitionParser 作用
解析springmvc各种各样的标签 如解析引入外部文件标签 List标签 Set标签 mvc:annotation-driven......