标签:value 封装 定义 i++ long 模块 容器 www percent
Spring MVC 通过反射机制对目标处理方法进行解析,将请 求消息绑定到处理方法的入参中。数据绑定的核心部件是 DataBinder
1、Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
2、DataBinder 调用装配在 Spring MVC 上下文中的ConversionService 组件进行数据类型转换、数据格式化工作 。将 Servlet 中的请求信息填充到入参对象中
3、调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData 对象
4、Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参(数据类型转换、数据格式化时,如果有错误,错误信息放在BindingResult 中)
简单看源码的心得,入参时POJO,给pojo的中属性的set方法打断点。
<mvc:annotation-driven /> 会自动注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter 与ExceptionHandlerExceptionResolver 三个bean
还将提供以下支持:
支持使用 ConversionService 实例对表单参数进行类型转换
支持使用 @NumberFormat annotation、@DateTimeFormat注解完成数据类型的格式化
支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
支持使用 @RequestBody 和 @ResponseBody 注解
由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
@InitBinder方法不能有返回值,它必须声明为void。
@InitBinder方法的参数通常是是 WebDataBinder。
//假设场景:在添加新的User对象是,在Controller添加如下方法,那么lastName属性值就是空 //不自动绑定对象中的lastName属性,另行处理 @InitBinder public void initBinder(WebDataBinder binder){ binder.setDisallowedFields("lastName"); }
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 ……
可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService。Spring 将自动识别出 IOC 容器中的 ConversionService,并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.jdy.mvc.MyConversionService"/> </list> </property> </bean>
Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactroyBean 中:
Converter<S,T>: | 将 S 类型对象转为 T 类型对象 |
ConverterFactory |
将相同系列多个 “同质” Converter 封装在一 起。如果希望将一种类型的对象转换为另一种类型及其子类的对 象 (例如将 String 转换为 Number 及 Number 子类 (Integer、Long、Double 等)对象)可使用该转换器工厂类 |
GenericConverter | 会根据源类对象及目标类对象所在的宿主类 中的上下文信息进行类型 |
<mvc:annotation-driven conversion-service=“conversionService”/> 会将自定义的 ConversionService 注册到Spring MVC 的上下文中。
<mvc:annotation-driven conversion-service=“conversionService”/> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.jdy.mvc.MyConversionService"/> </list> </property> </bean>
需求:自定义一个类型转换器,将字符串转成成Employee对象。
<form action="testConversionServiceConverer" method="POST"> <!-- lastname-email-gender-department.id 例如: GG-gg@jdy.com-0-105 --> Employee: <input type="text" name="employee"/> <input type="submit" value="Submit"/> </form>
@Component public class EmployeeConverter implements Converter<String, Employee> { @Override public Employee convert(String source) { if(source != null){ String [] vals = source.split("-"); //GG-gg@jdy.com-0-105 if(vals != null && vals.length == 4){ String lastName = vals[0]; String email = vals[1]; Integer gender = Integer.parseInt(vals[2]); Department department = new Department(); department.setId(Integer.parseInt(vals[3])); Employee employee = new Employee(null, lastName, email, gender, department); System.out.println(source + "--convert--" + employee); return employee; } } return null; } }
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven> <!-- 配置 ConversionService --> <bean id="conversionService" class="org.springframework.format.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="employeeConverter"/> </set> </property> </bean>
@RequestMapping("/testConversionServiceConverer") public String testConverter(@RequestParam("employee") Employee employee){ System.out.println("save: " + employee); employeeDao.save(employee); return "redirect:/emps"; }
Spring 在格式化模块中定义了一个实现 ConversionService 接口的 FormattingConversionService 实现类,该实现类扩展 了 GenericConversionService,因此它既具有类型转换的 功能,又具有格式化的功能
FormattingConversionService 拥有一个FormattingConversionServiceFactroyBean 工厂类,后者用于在 Spring 上下文中构造前者
FormattingConversionServiceFactroyBean 内部已经注册了 :
NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性 使用 @NumberFormat 注解
JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型 的属性使用 @DateTimeFormat 注解。装配了 FormattingConversionServiceFactroyBean 后,就可 以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动 了。 默认创建的 ConversionService 实例即为 FormattingConversionServiceFactroyBean
@DateTimeFormat 注解可对 java.util.Date、java.util.Calendar、java.long.Long 时间 类型进行标注:
pattern 属性:类型为字符串。指定解析/格式化字段数据的模式,如:”yyyy-MM-dd hh:mm:ss”
iso 属性:类型为 DateTimeFormat.ISO。指定解析/格式化字段数据 的ISO模式,包括四种:ISO.NONE(不使用) -- 默 认、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、 ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)
style 属性:字符串类型。通过样式指定日期时间的格式,由两位字 符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日 期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整 日期/时间格式、-:忽略日期或时间格式
@NumberFormat 可对类似数字类型的属性进行标注,它拥有两个互斥的属性:
style:类型为 NumberFormat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类)、Style.CURRENCY(货币类型)、 Style.PERCENT(百分数类型)
格式化示例
//step1:配置annotation-driven <mvc:annotation-driven /> //step2:在目标对象的属性上加注解 public class Employee { private Integer id; @NotEmpty private String lastName; @Email private String email; //1 male, 0 female private Integer gender; private Department department; @Past @DateTimeFormat(pattern="yyyy-MM-dd") private Date birth; @NumberFormat(pattern="#,###,###.#") private Float salary;
@RequestMapping(value="/emp", method=RequestMethod.POST) public String save(@Valid Employee employee, Errors result, Map<String, Object> map){//Errors 可以换成BindingResult System.out.println("save: " + employee); if(result.getErrorCount() > 0){ System.out.println("出错了!"); for(FieldError error:result.getFieldErrors()){ System.out.println(error.getField() + ":" + error.getDefaultMessage()); } //若验证出错, 则转向定制的页面 map.put("departments", departmentDao.getDepartments()); return "input"; } employeeDao.save(employee); return "redirect:/emps"; }
Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有JSR303标准的校验注解外,它还支持以下的扩展注解
如何校验
①. 使用 JSR 303 验证标准
②. 加入 hibernate validator 验证框架的 jar 包
③. 在 SpringMVC 配置文件中添加 <mvc:annotation-driven />
④. 需要在 bean 的属性上添加对应的注解
⑤. 在目标方法 bean 类型的前面添加 @Valid 注解
Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验
Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要 在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校 验的 Bean 中 。
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.7.Final</version> </dependency>
在已经标注了 JSR303 注解的表单/命令对象前标注一个 @Valid,Spring MVC 框架在将请求参数绑定到该入参对象 后,就会调用校验框架根据注解声明的校验规则实施校验
Spring MVC 是通过对处理方法签名的规约来保存校验结果 的:前一个表单/命令对象的校验结果保存到随后的入参 中,这个保存校验结果的入参必须是 BindingResult 或 Errors 类型,这两个类都位于org.springframework.validation 包中
Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 getFieldErrors(String field)
在目标方法中获取校验结果
在表单/命令对象类的属性中标注校验注解,在处理方法对 应的入参前添加 @Valid,Spring MVC 就会实施校验并将校 验结果保存在被校验入参对象之后的 BindingResult 或 Errors 入参中。
常用方法:
FieldError getFieldError(String field)
List<FieldError> getFieldErrors()
Object getFieldValue(String field)
Int getErrorCount()
即使处理方法的签名中没有对应于表单/命令对象的结果 入参,校验结果也会保存在 “隐含对象” 中。
隐含模型中的所有数据最终将通过 HttpServletRequest 的 属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取 错误信息
* Pattern.user.password
* Pattern.password
* Pattern.java.lang.String
* Pattern
* @RequiredParam(“param1”) 标注了一个入参,但是该参数不存在
* typeMismatch:在数据绑定时,发生数据类型不匹配的问题
* methodInvocation:Spring MVC 在调用处理方法时发生了错误
注册国际化资源文件
<!-- 配置国际化资源文件 --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="i18n"></property> </bean>
i18n.properties
NotEmpty.employee.lastName=^^LastName\u4E0D\u80FD\u4E3A\u7A7A. Email.employee.email=Email\u5730\u5740\u4E0D\u5408\u6CD5 Past.employee.birth=Birth\u4E0D\u80FD\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4. typeMismatch.employee.birth=Birth\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F. i18n.user=User i18n.password=Password
编写目标方法,使其返回 JSON 对应的对象或集合
@ResponseBody //SpringMVC对JSON的支持 @RequestMapping("/testJSON") public Collection<Employee> testJSON(){ return employeeDao.getAll(); }
增加页面代码:index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> <script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script> <script type="text/javascript"> $(function(){ $("#testJSON").click(function(){ var url = this.href ; var args = {}; $.post(url,args,function(data){ for(var i=0; i<data.length; i++){ var id = data[i].id; var lastName = data[i].lastName ; alert(id+" - " + lastName); } }); return false ; }); }); </script> </head> <body> <a href="empList">To Employee List</a> <br><br> <a id="testJSON" href="testJSON">testJSON</a> </body> </html>
测试
HttpMessageConverter<T>接口定义的方法:
Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对象,同时指定支持 MIME 类型(text/html,applaiction/json等)
Boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在MediaType 中定义。
List<MediaType> getSupportMediaTypes():该转换器支持的媒体类型。
T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。
void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类型为 contentType。
public interface HttpInputMessage extends HttpMessage { InputStream getBody() throws IOException; } public interface HttpOutputMessage extends HttpMessage { OutputStream getBody() throws IOException; }
DispatcherServlet 默认装配 RequestMappingHandlerAdapter ,而 RequestMappingHandlerAdapter 默认装配如下 HttpMessageConverter:
加入 jackson jar 包后, RequestMappingHandlerAdapter 装配的 HttpMessageConverter 如下:
默认情况下数组长度是6个;增加了jackson的包,后多个一个MappingJackson2HttpMessageConverter
标签:value 封装 定义 i++ long 模块 容器 www percent
原文地址:https://www.cnblogs.com/jdy1022/p/14612695.html