博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringMVC 之数据转换和国际化
阅读量:7253 次
发布时间:2019-06-29

本文共 11454 字,大约阅读时间需要 38 分钟。

1. 数据绑定流程

  1. SpringMVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory
    实例,以创建 DataBinder 实例对象;
  2. DataBinder 调用装配在 SpringMVC 上下文中的 ConversionService 组件进行数据类型转换,
    数据格式化工作; 将 Servlet 中的请求信息填充到入参对象中;
  3. 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果
    BindingData 对象;
  4. Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参;
  5. 总结: Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中.
    数据绑定的核心部件是 DataBinder.

1222878-20171103222907779-2135331546.png

2. Spring 支持的转换器

  • Spring 定义了三种类型的转换器接口,实现任意一个转换接口都可以作为自定义转换器注册到
    ConversionServiceFacotoryBean 中:
    • Converter<S,T>: 将 S 类型的对象转换为 T 类型对象;
    • ConverterFactory: 将相同系列多个"同质"Converter封装在一起; 将一种类型的对象转换为
      另一种类型及其子类的对象.例如,将String转换为 Number 及 Number 子类Integer,Long,
      Double等对象;
    • GenericConverter: 会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换;

2.1 自定义类型转换器

  • ConversionService 是 Spring 类型转换体系的核心接口;
  • 可以利用 ConversionServiceFatoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService;
    Spring 将自动识别出 IOC 容器中的 ConversionService, 并在 Bean 属性配置及 SpringMVC
    处理方法入参绑定等场合使用它进行数据的转换;
  • 可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器;
// 从 java.lang.String 转换为 POJO 类// index.jsp
// EmployeeHanlder.java@RequestMapping(value="/empConvert",method=RequestMethod.POST)public String empConvert(@RequestParam("employee") Employee employee){ employeeService.save(employee); return "redirect:/emps";}// EmployeeConverter.java// 类型转换,将String类型转换为 Employee, 即自定义类型转换器@Componentpublic class EmployeeConverter implements Converter
{ public Employee convert(String source){ Employee result = null; if(null != resource){ // 将字符串分割 String[] empInfos = source.split(";"); if(null != empInfos && empInfos.length == 4){ result = new Employee(); result.setLastName(empInfos[0]); result.setEmail(empInfos[1]); // 将String 类型转换为 Integer 类型 result.setGender(Integer.parseInt(empInfos[2])); Department department = new Department(); department.setId(Integer.parseInt(empInfos[3])); result.setDepartment(department); } } return result; }}// SpringDispatcherServlet-servlet.xml// 在SpringMVC配置文件中,注册自定义类型转换器

2.2 mvc:annotation-driven

  1. <mvc:annotation-driven/>会自动注册 RequestMappingHandlerMapping,
    RequestMappingHandlerAdapterExceptionHandlerExceptionResolver 三个Bean;
  2. 还提供以下支持:
    • 支持使用 ConversionService 实例对表单参数进行类型转换;
    • 支持使用 @NumberFormatannotation, @DataTimeFormat 注解完成数据类型的格式化;
    • 支持使用 @Valie 注解对 JavaBean 实例进行 JSR 303 验证;
    • 支持使用 @RequestBody@ResponseBody 注解;

2.3 @InitBinder

  • @InitBinder标识的方法,可以对 WebDataBinder 对象进行初始化,WebDataBinder 是 DataBinder
    的子类,用于完成由表单字段到 JavaBean 属性的绑定;
  • @InitBinder方法不能有返回值,它必须声明为 void;
  • @InitBinder 方法的参数通常是 WebDataBinder
@InitBinderpublic void initBinder(WebDataBinder webDataBinder){    // 不自动绑定对象中的 email 属性    webDataBinder.setDisallowedFields("email");}

1222878-20171103222954935-1365322922.png

3. 数据格式化

  1. 对属性对象的输入/输出进行格式化,从其本质上讲依然属于"类型转换"的范畴;
  2. Spring 在格式化模块中定义了一个实现了ConversionService接口的FormattingConversionService
    实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,也具有格式化
    的功能;
  3. FormattingConversionService拥有一个FormattingConversionServiceFactoryBean
    厂类,后者用于在 Spring上下文中构造前者;
  4. FormattingConversionServiceFactoryBeanConversionServiceFactoryBean比较
    • ConversionService只有数据转换功能,与 ConversionServiceFactoryBean对应;
    • FormattingConversionService具有数据转换和数据格式化功能,
    • FormattingConversionServiceFactoryBean既可以注册自定义的转换器,也可以注册自定义
      的注解驱动器逻辑;
  5. <mvc:annotation-driven>标签内部默认创建的一个 conversionService 实例就是一个
    FormattingConversionServiceFactoryBean;
// 日期类: @DateTimeFormat// 数字类: @NumberFormatpublic class Employee{    private Integer id;    private String lastName;    private String email;    // 更改输入的日期格式, 默认格式为 yyyy/MM/dd    @DateTimeFormat(pattern="yyyy-MM-dd")    private Date birth;    @NumberFormat(patter="#,###.##")    private Double salary;    get和set 方法}// SpringDispatcherServlet-servlet.xml// 配置 FormattingConversionServiceFacotryBean

3.1 BindingResult

  • BindingResult 是接口,继承Errors;
// 保存客户@RequestMapping(value="/emp",method=RequestMethod.POST)public String save(Employee employee,BindingResult bindingResult){    if(bindingResult.getErrorCount()>0){        List
list = bindingResult.getFieldErrors(); for(FieldError fieldError : list){ System.out.println(fieldError.getField()+"\t"+fieldError.getCode()); } throw new RuntimeException("录入信息出错!"); } employeeService.save(employee); return "redirect:/emps";}

1222878-20171103223024388-397019789.png

1222878-20171103223033138-1076476295.png

1222878-20171103223042216-251920701.png

3.2 JSR 303 数据验证

  1. JSR303 是Java为Bean数据合法性校验提供的标准框架,它已经包含在 JavaEE6.0 中;
  2. JSR303 通过在 Bean 属性上标注类似于 @NotNull,@Max等标准的注解指定校验规则,并通过
    标准的验证接口对 Bean 进行验证;
  3. Hibernate Validator 是 JSR303 的一个具体实现,除支持所有标准的校验注解外,它还支持以下的
    扩展注解
    • @Email: 被注释的元素必须是邮箱地址;
    • @Length: 被注释的字符串大小必须在指定的范围内;
    • @NotEmpty: 被注释的字符串必须非空;
    • @Range: 被注释的元素必须在合适的范围内;
  4. 在 SpringMVC 中,可直接通过注解驱动的方式进行数据校验;
  5. Spring 的 LocalValidatorFactoryBean, 既实现了 Spring 的 Validator 接口,也实现了
    JSR303 的 Validator 接口.只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,
    即可将其注入到需要数据校验的Bean中;
  6. Spring 本身并没有提供JSR303的实现,所以必须将JSR的实现者的jar包放到类路径下;
// 步骤:    /*     * 1. 导入 HibetnateValidator 验证框架的 jar 包(5个):     *    classmate-0.8.0.jar,   hibernate-validator-5.0.0.CR2.jar,     *    hibernate-validator-annotation-processor-5.0.0.CR2.jar,     *    jboss-logging-3.1.1.GA.jar,  validation-api-1.1.0.CR1.jar     *     * 2. 添加注解 
, 会默认装配好一个 * LocalValicatorFactoryBean * 3. Employee.java 中添加 Field 验证要求的相关注解;比如名字不为空, Email格式要合法等; * 4. Save 方法对应的 POJO 参数前面添加 @Valid; * 5. 发生异常后,重新回到录入页面; */ // Employee.java pubic class Employee{ private Integer id; @NotEmpty private String lastName; // 名字不能为空 @Email private String email; // 校验Email格式 .....(略) } // EmployeeHandler.java public String save(@Valid Employee employee,BindingResult bindingResult, Map
map){ if(bindingResult.getErrorCount()>0){ List
list = bindingResult.getFieldErrors(); for(FieldError fieldError : list){ System.out.println(fieldError.getField()+"\t" +fieldError.getDefaultMessage()); } // 返回到登录页面 // 1. 查询出全部部门 map.put("departments",departmentService.getDepartments()); // 2. 查询出性别 map.put("genders",getGenderUtils()); return "input"; } employeeService.save(employee); return "redirect:/emps"; } // 页面显示错误信息 // index.jsp
lastName:
  
email:
  
// 自定义错误信息// SpringDispatcherServlet-servlet.xml 配置 国际化信息
// 在src目录下,新建 i18n.properties// 命名规则: 注解名+类名+需要限制的fieldNotEmpty.employee.lastName=用户名不能为空Email.employee.email=邮件地址格式不正确

3.3 SpringMVC 数据校验

  • 需校验的 Bean 对象和其绑定结果对象或错误对象是成对出现的,它们之间不允许声明其他的参数;
  • Errors 接口提供了获取错误信息的方法, 如getErrorCount()getFieldErrors(String field)
  • BindingResult 扩展了 Errors 接口;

1222878-20171103223113232-1924566564.png

4. 处理JSON

  1. 导入 jar 包:
    • jackson-annotations-2.7.0.jar
    • jackson-core-2.7.0.jar
    • jackson-databind-2.7.0.jar
  2. 编写目标方法,使其返回 JSON 对应的对象(POJO)或集合(Map);
  3. 在方法上添加@ResponseBody注解;
// 需求: 查询所有员工// Demo.javapublic class Demo{    @Autowired    private EmployeeService employeeService;    // 导入 jackson 的jar包,并且标注 @ResponseBody    // HttpConverter 会自动识别出以 JSON 格式返回给前台    @ResponseBody    @RequestMapping(value="/testJson",method=RequestMethod.GET)    public Collection
qryAllEmployee(){ // 直接返回 Collection 集合 return employeeService.getAll(); }}// index.jsp
JSON 示例:
点击这里

1222878-20171103223146560-2090627408.png

4.1 HttpMessageConverter

  1. HttpMessageConverter<T>接口负责将请求信息转换为一个对象(类型为 T),或者将对象(类型为 T)
    输出为响应信息;
  2. HttpMessageConverter<T>接口定义的方法:
    • Boolean canRead(Class<?>clazz, MediaType mediaType): 表示转换器是否可以将请求信息
      转换为 clazz 类型的对象,同时,指定支持 MIME 类型(text/html,application/json等);
    • Boolean canWrite(Class<?>clazz, MediaType mediaType): 表示转换器是否可以将 clazz
      类型的对象写入到响应流中;
    • T read(Class<? extends T>clazz,HttpInputMessage msg): 将请求信息流转换为 T 类型的
      对象;
    • void write(T t, MediaType contentType, HttpOutputMessage msg): 将 T 类型的对象
      写到响应流中,同时,指定响应的媒体类型为 contentType;
  3. 常见HttpMessageConverter<T>的实现类
    • StringHttpMessageConverter: 将请求信息转换为字符串;
    • XmlAwareFormHttpMessageConverter
    • ByteArrayHttpMessageConverter: 读写二进制数据;
    • SourceHttpMessageConverter: 读写 javax.xml.transform.Source 类型的数据;
  4. Spring 中使用 HttpMessageConverter<T>:
    • 使用 @RequestBody/@ResponseBody 对处理方法进行标注;
    • 使用 HttpEntity<T>/ResponseEntity<T>作为处理方法的入参或返回值;
  5. 当控制处理方法使用到@RequestBody/@ResponseBodyHttpEntity<T>/ResponseEntity<T>
    时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter, 进而
    根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的
    HttpMessageConverter, 将报错;
  6. @RequestBody@ResponseBody 不需要成对出现;

1222878-20171103223205607-735249590.png

// 示例一: @RequestBody// index.jsp

注册

用户名:
密码:
上传文件:
// Demo.java@RequestMapping(value="/testRequestBody",method=RequestMethod.POST)public String testRequestBody(@RequestBody String content){ System.out.println("请求内容为:"+content); return "ok";}

1222878-20171103223225529-1734735237.png

// 示例二: 下载 @ResponseEntity
// index.jsp下载示例:
下载// Demo.java@RequestMapping(value="/testDownload", method=RequestMethod.GET)public ReponseEntity
testDownload() throws IOException{ byte[] body = null; FileInputStream input = new FileInputStream(new File("/Users/用户名/Documents/a.js")); body = new byte[input.available()]; input.read(body); input.close(); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition","attachment;filename=a.js"); HttpStatus statusCode = HttpStatus.OK;ResponseEntity
result = new ResponseEntity
(body,headers,statusCode); return result;}

5. 国际化

  • AcceptHeaderLocaleResolver: 根据 HTTP 请求头的Accept-Language参数确定本地化类型,
    如果没有显示定义本地化解析器,SpringMVC默认使用该解析器;
  • CookieLocaleResolver: 根据指定的 Cookie 值确定本地化类型;
  • SessionLocaleResolver: 根据 Session 中特定的属性确定本地化类型;
  • LocaleChangeInterceptor: 从请求参数中获取本次请求对应的本地化类型;
// 第一种方式// src 目录下,新建 i18n.properties, i18n_en_US.properties, i18n_zh_CN.properties// i18n.properties 和 i18n_en_US.propertiesi18n.username=usernamei18n.password=password// i18n_zh_CN.propertiesi18n.username=用户名i18n.password=密码// index.jsp国际化:// ok.jsp用户名: 
密 码:
// SpringDispatcherServlet-servlet.xml 配置
// Demo.java@RequestMapping(value="/i18n",method=RequestMethod.GET)public String test(){ return "ok";}// 第二种方式: 注入ResourceBundleMessageSource// Demo.javapublic class Demo{ @Autowired private ResourceBundleMessageSource messageSource; @RequestMapping(value="/i18n2",method=RequestMethod.GET) public String test2(Locale locale){ String v1 = messageSource.getMessage("i18n.username",null,locale); String v2 = messageSource.getMessage("i18n.password",null,locale); System.out.println(v1+"...."+v2); return "ok"; }}// 第三种方式, 配置 SessionLocalResolver + LocaleChangeInterceptor// springDispatcherServlet-servlet.xml
// index.jsp

示例

中文
英文
// Demo.javapublic class Demo{ @RequestMapping(value="/test3",method=RequestMethod.GET) public String test3(){ return "ok"; }}

5.1 SessionLocaleResolver & LocaleChangeInterceptor 的工作原理

1222878-20171103223258107-428845169.png

参考资料

转载于:https://www.cnblogs.com/linkworld/p/7780901.html

你可能感兴趣的文章
LeetCode - 37. Sudoku Solver
查看>>
公钥,私钥和数字签名这样最好理解
查看>>
SqlBulkCopy 类
查看>>
Java知多少(104)网络编程之统一资源定位符URL
查看>>
csu1811(树上启发式合并)
查看>>
spring 整合maven 定时任务调度,多个任务写法
查看>>
New Concept English Two 15 37
查看>>
L125
查看>>
poj2192
查看>>
json的内容回顾
查看>>
SAP将内表生成XML作为excel保存到FTP
查看>>
栅格系统
查看>>
[Selenium] 使用自定义的FirefoxProfile
查看>>
Spine批量导出Command line Export
查看>>
POJ3690:Constellations——题解
查看>>
第二次毕业设计任务书
查看>>
sts安装出现could not find jar:file解决办法
查看>>
Maven中POM.XML详解
查看>>
小时候,长大了
查看>>
一次服务器被入侵后的分析
查看>>