SpringMVC--数据转换、格式化和校验

来源:互联网 发布:我知主掌握明天歌谱 编辑:程序博客网 时间:2024/05/21 22:45

Spring MVC通过反射机制对目标处理方法的签名进行分析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder,运行机制如下:
这里写图片描述
1、SpringMVC主框架将ServletRequest对象及处理方法的入参对象实例传递给DataBinder。
2、DataBinder调用ConversionService组件进行数据类型转换、数据格式化等操作,将ServletRequest中的信息填充到入参对象中。
3、DataBinder调用Validator组件对已经绑定了请求消息数据的入参对象进行数据合法性校验。
4、最终生成数据绑定结果BindingResult对象。该对象既包括已完成数据绑定的入参对象,还包含相应的校验错误对象。

一、ConversionService
ConversionService是Spring类型转换体系的核心接口,可以利用ConversionServiceFactoryBean在Spring的上下文中定义一个ConversionService。Spring将自动识别出上下文中的ConversionService,并在Bean属性配置及SpringMVC处理方法入参绑定等场合使用它进行数据转换。

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"/>

该FactoryBean创建ConversionService内建的很多转换器,包括String、Number、Array、Collection、Map、Properties及Object之间的转换器。

也可以自定义转换器,但是需要实现3种不同的转换器接口,实现任意一个转化器接口都可以作为自定义转化器注册到ConversionServiceFactoryBean中:
1、Converter<\S,T>接口
将S类型的对象转换为T类型的对象
2、ConverterFactory<\S,R>接口
将一种类型的对象转换为另一种类型及其子类的对象。比如,将String类型转换为Number及Number子类(Integer/Long/Double等)对象。
3、GenericConverter接口
根据源对象及目标类对象所在宿主类的上下文信息进行类型转换工作。它有一个实现类ConditionalGenericConverter,有一个方法可以根据宿主类的上下文信息决定是否进行类型转换。

1、自定义一个转化器

//实现Converter<S,T>接口public class StringToUserConverter implements Converter<String,User> {    public User convert(String source){        User user=new User();        if(source!=null){            String [] items=source.split(":");            user.setUserName(items[0]);            user.setPassword(items[1]);        }        return user;    }}

2、在Spring上下文中装配该自定义的转换器

<!--该标签会默认创建RequestMappingHandlerMapping和RequestMappingHandlerAdapter-->    <!--也会默认创建ConversionService,也就是FormattingConversionServiceFactoryBean--><!--如果有自定义,会覆盖默认的。因为要使用自定义的ConversionService,需要显式定义一个ConversionService覆盖默认的设置-->        <mvc:annotation-driven conversion-service="conversionService" /><!--converters属性可以接受Converter/ConverterFactory/GenericConverter/ConditionGenericConverter接口的实现类--><bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">    <property name="converters">        <bean class="com.smart.util.StringToUserConverter"/>    </property></bean>

3、Controller中

 @ResponseBody    @RequestMapping(value = "/hand77")    public String hand77(@RequestParam("user") User user){        Gson gson=new Gson();        String ujson=gson.toJson(user);        return ujson;    }

4、输入访问URL:http://localhost:8080/spitter/test/hand77?user=test:444,结果是:
这里写图片描述

对于这种参数之间用特殊符号链接的,可以使用转换器进行转换,但是对于规范化的参数,比如XML/JSON格式的,直接使用HttpMessageHandlerAdapter进行转换。

二、数据格式化—使用注解的方式,格式化本质上也是类型转换

Spring提供了一个AnnotationFormatterFactory<\A extends Annotation>接口,并且提供了两个内建的实现类,分别支持数字类型和日期类型的注解驱动格式化:
2.1、NumberFormatAnnotationFormatterFactory:支持对数字类型的属性使用@NumberFormat注解
2.2、JodaDateTimeFormatAnnotationFormatterFactory:支持对日期类型的属性使用@DateTimeFormat注解

Spring中有一个实现ConversionService接口的FormattingConversionService实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,又具有格式化功能。

FormattingConversionService对应也有一个工厂类FormattingConversionServiceFactoryBean,通过这个工厂类,既可以注册自定义的转换器,还可以注册自定义的注解驱动。

<\mvc:annotation-driven/>标签内部默认创建的ConversionService实例就是一个FormattingConversionServiceFactoryBean。

FormattingConversionServiceFactoryBean在内部会自动注册NumberFormatAnnotationFormatterFactory 和 JodaDateTimeFormatAnnotationFormatterFactory ,因此,装配了FormattingConversionServiceFactoryBean后,就可以使用注解驱动的格式化功能。

1、在Spring上下文中装配FormattingConversionServiceFactoryBean

<!--因为要装配自定义转换器,所以显式定义,如果没有自定义转化器,直接使用默认就行--><mvc:annotation-driven conversion-service="conversionService" /><!--既可以装配自定义的转换器,也可以使用注解格式化功能--><bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">    <property name="converters">        <bean class="com.smart.util.StringToUserConverter"/>    </property></bean>

2、在目标类上标注格式化注解

public class Work {    private String name;   // 可将形如1989-09-19的字符串转换到Date类型的属性中    @DateTimeFormat(pattern = "yyyy-MM-dd")    private Date birthday;    //可将形如1,222.22的字符串转换到Long类型的属性中    @NumberFormat(pattern = "#,###.##")    private long salary;    //省略getter/setter方法}

3、Controller中

@ResponseBody    @RequestMapping(value = "/hand99")    public String hand99(Work work){        Gson gson=new Gson();        String ujson=gson.toJson(work);        return ujson;    }

4、请求url:http://localhost:8080/spitter/test/hand99?name=lisi&birthday=1999-7-3&salary=1,222.33,结果:
这里写图片描述

三、数据校验
JSR-303定义了一套可标注在成员变量、属性方法上的校验注解:
这里写图片描述
Hibernate Validator既支持所有的标注校验注解,还支持如下的扩展注解:
这里写图片描述

Spring拥有独立的数据校验框架,也支持JSR-303标注的校验框架。它提供了一个重要接口Validator。
LocalValidatorFactoryBean既实现了Validator接口,也实现了JSR-303的Validator接口。所有只要在容器上下文中定义一个LocalValidatorFactoryBean就可以将其注入需要数据校验的Bean中。

    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

但是<\mvc:annotation-driven/>会默认装配一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解,即可让Spring Mvc在完成数据绑定后执行数据校验规则

注意:Spring本身没有提供JSR-303的实现,必须将JSR-303的实现者(Hibernate Validator)的JAR文件放到类路径下,Spring会自动加载并装配。

<dependency>    <groupId>org.hibernate</groupId>    <artifactId>hibernate-validator</artifactId>    <version>5.4.1.Final</version></dependency>

1、在目标类上添加校验注解

public class User {    //通过正则表达式进行校验,匹配4-30个包含数字、字母及下划线的字符    @Pattern(regexp = "w{4,30}")    private String userName;    //扩展的校验注解,将属性值的长度限制在2-10之间    @Length(min=2,max = 10)    private String password;}

2、在Controller中

@RequestMapping(value = "/hand33")//需校验的表单/命令对象(User)和其绑定结果对象(BindingResult )或错误对象(Errors)是成对出现的,之间不允许声明其他入参    public String hand33(@Valid @ModelAttribute("user") User user, BindingResult bindingResult){    //判断是否存在错误        if(bindingResult.hasErrors()){            return "user/login";        }else{            return "user/createSuccess";        }    }

3、显示错误信息
SpringMVC除了将表单/命令对象的校验结果保存到对于的BindingResult或Errors对象中外,还将所有的校验结果保存到隐含模型中。隐含模型中的所有数据最终将通过HttpServletRequest的属性列表暴露给JSP视图对象。

在Spring的form表单中:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %><body>   <form:form modelAttribute="user" action="/spitter/test/hand33">   <!--显示所有的校验错误信息-->       <form:errors path="*" cssClass="errorClass"/>       <table>           <tr>               <td>用户名:</td>               <td>                   <form:input path="userName"/>                   <form:errors path="userName" cssClass="errorClass"/>               </td>           </tr>           <tr>               <td>密码:</td>               <td>                   <form:password path="password"/>                   <form:errors path="password" cssClass="errorClass"/>               </td>           </tr>           <tr>               <td>                   <input type="submit" name="提交">               </td>           </tr>       </table>   </form:form></body>

结果:
这里写图片描述

原创粉丝点击