SpringMVC数据类型转换、格式化、JSR303校验

标签:?springmvc

1. 数据绑定流程

  SpringMVC通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。其中,数据绑定的核心部件是DataBinder,运行机制如下:
  这里写图片描述

  数据绑定的具体流程说明如下:

  • SpringMVC主框架将ServletRequest对象和目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象;
  • DataBinder调用装配在SpringMVC上下文中的ConversionService组件进行数据类型转换与格式化操作,并将Servlet中的请求信息填充到入参对象中;
  • SpringMVC进而调用Validator组件对已经绑定请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData对象;
  • SpringMVC抽取BindingResult中的入参对象和校验错误对象,将其赋给处理方法的响应入参中。
// ModelAttributeMethodProcessor的resolveArgument()方法的核心代码:
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
    bindRequestParameters(binder, request); //绑定请求数据到入参中
    validateIfApplicable(binder, parameter); //校验入参中数据的合法性
    if (binder.getBindingResult().hasErrors()) {
        if (isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
        }
    }
}

?


2. 数据类型转换

  SpringMVC上下文中内建了许多数据类型转换器,可完成大多数Java类型的换工作,具体可通过调试模式查看binder的conversionService属性值。
  这里写图片描述


3. 自定义数据类型转换器

  明确需求:

// 将表单输入的name-email-gender-department.id亚博yabo线上投注转换为Employee对象
@RequestMapping("/testEmployeeConverter")
public String testEmployeeConverter(Employee employee) {
    System.out.println(employee);
    return "redirect:/emps";
}

?

  第一步:自定义实现Converter接口的数据类型转换器类,并添加到Spring的IoC容器中;

package com.qiaobc.springmvc.converter;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import com.qiaobc.springmvc.domain.Department;
import com.qiaobc.springmvc.domain.Employee;

// 三种类型的数据转换器接口:Converter、ConverterFactory、GenericConverter
@Component
public class EmployeeConverter implements Converter{

    @Override
    public Employee convert(String source) {
        if(source != null) {
            String[] strs = source.split("-");
            if(strs != null && strs.length == 4) {
                String name = strs[0];
                String email = strs[1];
                String gender = strs[2];
                Department department = new Department();
                department.setDeptId(Integer.parseInt(strs[3]));
                return new Employee(null, name, email, gender, department);
            }
        }
        return null;
    }

}

?

  第二步:在SpringMVC的配置文件中,通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器;



    
        
            
        
    

?

  第三步:通过mvc:annotation-driven标签的conversion-service属性,将ConversionServiceFactoryBean注册到SpringMVC的上下文中。

?


4. 关于mvc:annotation-driven

   会自动注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter与ExceptionHandlerExceptionResolver三个bean,其还将提供如下支持:

  • 支持使用ConversionService实例对表单参数进行类型转换;
  • 支持使用@NumberFormat@DateTimeFormat注解完成数据类型的格式化;
  • 支持使用@Valid注解对JavaBean实例进行JSR 303验证;
  • 支持使用@RequestBody@ResponseBody注解。

这里写图片描述


5. @InitBinder注解

  由@InitBinder注解标识的方法,可以对WebDataBinder对象进行初始化;WebDataBinder是DataBinder的子类,用于完成由表单字段到JavaBean属性的绑定。注意@InitBinder方法不能有返回值,其入参通常是WebDataBinder对象。

/**
 * 在进行数据绑定时,不自动绑定对象中的name属性
 * 注意:其对自定义类型转换器并不起作用
 * @param binder
 */
@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.setDisallowedFields("name");
}

?


6. 数据格式化

6.1 FormattingConversionServiceFactroyBean

  主要作用:用于在Spring上下文中构造FormattingConversionService。
  其中,FormattingConversionServiceFactroyBean工厂类的内部已经注册:

  • NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用@NumberFormat注解;
  • JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用@DateTimeFormat注解;

6.2 mvc:annotation-driven

  • mvc:annotation-driven默认创建的ConversionService实例即为FormattingConversionServiceFactroyBean;
  • 故配置mvc:annotation-driven后即可在SpringMVC入参绑定及模型数据输出时使用注解驱动,以实现数据类型转换和数据格式化。

6.3 FormattingConversionService

  • 该类是Spring格式化模块中实现了ConversionService接口的实现类;
  • 该类扩展了GenericConversionService,既具有类型转换的功能,又具有格式化的功能。

6.4 日期格式化

  @DateTimeFormat注解可对java.util.Date、java.util.Calendar和java.long.Long时间
类型的属性进行标注,其具有如下属性:

  • pattern属性:亚博yabo线上投注类型,用于指定解析/格式化字段数据的模式,如”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属性:亚博yabo线上投注类型,用于指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式;S表示短日期/时间格式、M表示中日期/时间格式、L表示长日期/时间格式、F表示完整日期/时间格式、-表示忽略日期或时间格式。

6.5 数值格式化

  @NumberFormat注解可对类似数字类型的属性进行标注,其具有两个互斥的属性:

  • pattern属性:亚博yabo线上投注类型,用于自定义样式,如”#,###,###.#”;
  • style属性:类型为NumberFormat.Style,用于指定样式类型,包括Style.NUMBER(正常数字类型)、Style.CURRENCY(货币类型)、Style.PERCENT(百分数类型)。
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;
@NumberFormat(pattern="###,###,###.##")
private float salary;

?


7. JSR303数据校验

  JSR 303 是Java为Bean数据合法性校验提供的标准框架,其已经包含在JavaEE 6.0中;JSR 303 通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
  JSR 303 支持的校验注解如下:

注解 功能说明
@Null 被注释的元素必须为null
@NotNull 被注释的元素必须不为null
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value) 被注释的元素必须是一个数字,其值必须大于或等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于或等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于或等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于或等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits(integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式

  Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,还支持一下扩展注解:

注解 功能说明
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的亚博yabo线上投注的大小必须在指定的范围内
@NotEmpty 被注释的亚博yabo线上投注必须非空
@Range 被注释的元素必须在合适的范围内

8. SpringMVC数据校验

8.1 基本概念

  ① 关于所需要的jar包
  Spring4.0拥有独立的数据校验框架,同时支持 JSR 303 标准的校验框架;但其本身并没有提供 JSR 303 的实现,故必须将 JSR 303 的实现者的jar包放到类路径下

  ② 关于LocalValidatorFactoryBean工厂类
  该工厂类既实现了Spring的Validator接口,也实现了 JSR 303 的Validator接口;故需要在 Spring 容器中定义LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean中。

  ③ 关于@Valid注解
  会默认装配LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解即可让SpringMVC在进行数据绑定时,同时调用校验框架完成数据校验工作。

  ④ 关于校验结果
  前一个表单/命令对象的校验结果保存到随后处理方法的入参中,该入参必须是BindingResult或Errors类型;且需注意,需校验的Bean对象和其绑定结果对象或错误对象是成对出现的,其之间不允许声明其他的入参。

8.2 具体实现

  第一步:添加Hibernate Validator验证框架所依赖的jar包,具体如下图所示:
    这里写图片描述
  第二步:在SpringMVC配置文件中添加mvc:annotation-driven标签;
  第三步:在需要校验的JavaBean属性上添加相应的校验注解,以Employee为例:

public class Employee {

    @NotNull
    private String name;

    @Email
    private String email;

    @Past
    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date birth;

    // ……
}

?

  第四步:在处理器目标方法的Bean类型的入参前添加@Valid注解,并添加保存校验结果的对象:

@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid Employee employee, BindingResult result, Map map) {
    System.out.println(employee);

    if(result.getErrorCount() > 0) {
        for(FieldError error : result.getFieldErrors()) {
            System.out.println(error.getField() + " : " + error.getDefaultMessage());
        }

        // 指定校验错误时所转向的定制页面
        map.put("departments", departmentDao.getDepartments());
        return "emp-edit";
    }

    employeeDao.save(employee);
    return "redirect:/emps";
}

?


9. 校验错误消息的显示

  SpringMVC还会将所有校验结果保存到隐含模型中,该模型中的所有数据最终将通过HttpServletRequest的属性列表暴露给JSP视图对象,故在JSP页面上可以获取错误信息。



    
    

Name:

Email:

Birthday:

?


10. 校验错误消息的国际化

  当某属性校验失败后,SpringMVC校验框架会为该属性生成4个消息代码,其以校验注解类名为前缀,结合modelAttribute、属性名及属性类型名;如NotNull.employee.name、NotNull.name、NotNull.java.lang.String和NotNull。
  错误代码前缀除了校验注解类名外,还有如下几种:

错误代码前缀 具体说明
required 必要的参数不存在;如@RequiredParam(“name”)标注入参,但该参数不存在时发生的错误
typeMismatch 在数据绑定时,发生数据类型转换或格式化错误
methodInvocation SpringMVC在调用处理方法时发生错误

 具体实现步骤:
 第一步:创建国际化资源文件i18n.properties,文件内容如下所示:

NotNull.employee.name=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
Email.employee.email=\u7535\u5B50\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
Past.employee.birth=\u51FA\u751F\u65E5\u671F\u4E0D\u80FD\u662F\u5C06\u6765\u65F6\u95F4

第二步:在SpringMVC配置文件中注册国际化资源文件,具体如下:



    

说明:当使用SpringMVC标签显示错误消息时,SpringMVC先查看WEB上下文是否装配对应的国际化消息,若有则显示国际化消息,否则显示默认的错误消息。

原文链接:加载失败,请重新获取