Spring MVC 数据绑定流程分析

1.    数据绑定流程原理★

①   Spring MVC 主框架将 ServletRequest  对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象

②   DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中

③   调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象

④   Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参

Spring MVC 通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是 DataBinder,运行机制如下:

原理:SpringMVC如何确定POJO参数的值,以及将页面的值正确的赋值给POJO
1、使用数据绑定器来负责将页面带来的参数绑定到pojo中
复制代码
binderFactory
//绑定器工厂根据当前请求,其他信息;创建出数据绑定器;
//数据绑定器负责将请求中的数据绑定到pojo中
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
        if (binder.getTarget() != null) {
             //数据绑定期间进行类型转换以及格式化工作
            bindRequestParameters(binder, request);
             //数据校验:email;birth;BindingResult组件中会封装错误信息;
            validateIfApplicable(binder, parameter);
             //数据校验错误信息处理
            if (binder.getBindingResult().hasErrors()) {
                   //如果出错有处理
                if (isBindExceptionRequired(binder, parameter)) {
                                         \\parameter指目标方法正在处理的当前参数  saveEmp(Employee emp)
    protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
        int i = parameter.getParameterIndex();//获取参数索引
        Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();//获取当前方法所有参数的参数类型
         
          //如果下一个参数是Errors旗下的,就返回成功;否则就是没人处理;
        boolean hasBindingResult =
               (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));

        return !hasBindingResult;
    }
                    
                     //如果没人处理就抛异常
                    throw new BindException(binder.getBindingResult());
                }
            }
        }
复制代码
2、DataBinder中有几个组件负责完成这几项工作;
3、ConversionService负责进行类型转换以及格式化工作

 

复制代码
SpringMVC默认使用的是DefaultFormattingCOnversionService
ConversionService converters =  负责数据类型转换及格式化;都是里面的每一个转换器负责工作;
    @org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@7c515e0,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    @org.springframework.format.annotation.DateTimeFormat java.time.LocalDate -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.time.LocalDate -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@34d07c25
    @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.time.LocalDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@2fbd9c99
    @org.springframework.format.annotation.DateTimeFormat java.time.LocalTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.time.LocalTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@317cb8e5
    @org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.time.OffsetDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@4c6fb0c7
    @org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.time.OffsetTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@616a3ae3
    @org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.time.ZonedDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@48de4b92
    @org.springframework.format.annotation.DateTimeFormat java.util.Calendar -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@7c515e0
    @org.springframework.format.annotation.DateTimeFormat java.util.Date -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@7c515e0
    @org.springframework.format.annotation.NumberFormat java.lang.Double -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    @org.springframework.format.annotation.NumberFormat java.lang.Float -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    @org.springframework.format.annotation.NumberFormat java.lang.Integer -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    @org.springframework.format.annotation.NumberFormat java.lang.Short -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    @org.springframework.format.annotation.NumberFormat java.math.BigDecimal -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    @org.springframework.format.annotation.NumberFormat java.math.BigInteger -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@1ae88e8d
    java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@15499bcc
    java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@314796e3
    java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@67a9e5f5
    java.lang.Long -> java.util.Calendar : org.springframework.format.datetime.DateFormatterRegistrar$LongToCalendarConverter@2425e96f
    java.lang.Long -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$LongToDateConverter@37be4c53
    java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@64e80876
    java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@24d24a7d
    java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@2d2b9f78
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.lang.Long: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@7c515e0,java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Long: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDate: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.lang.String -> java.time.LocalDate: org.springframework.format.datetime.standard.TemporalAccessorParser@1b95054a
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.lang.String -> java.time.LocalDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@1cb17371
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.LocalTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.lang.String -> java.time.LocalTime: org.springframework.format.datetime.standard.TemporalAccessorParser@183beaef
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.lang.String -> java.time.OffsetDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@5b5278c0
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.OffsetTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.lang.String -> java.time.OffsetTime: org.springframework.format.datetime.standard.TemporalAccessorParser@43da3826
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.time.ZonedDateTime: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@7bfa429f,java.lang.String -> java.time.ZonedDateTime: org.springframework.format.datetime.standard.TemporalAccessorParser@ff5bcf4
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Calendar: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@7c515e0
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Date: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@7c515e0
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Double: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Float: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Integer: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Short: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigDecimal: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigInteger: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@592e7da0
    java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@1795e212
    java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@7f0a5c2
    java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@3ee79e1e
    java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@35ac0acf
    java.lang.String -> java.time.Instant: org.springframework.format.datetime.standard.InstantFormatter@72824112
    java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@5f141e60
    java.lang.String -> java.util.Properties : org.springframework.core.convert.support.StringToPropertiesConverter@34316a3
    java.lang.String -> java.util.UUID : org.springframework.core.convert.support.StringToUUIDConverter@54f52170
    java.time.Instant -> java.lang.String : org.springframework.format.datetime.standard.InstantFormatter@72824112
    java.time.ZoneId -> java.util.TimeZone : org.springframework.core.convert.support.ZoneIdToTimeZoneConverter@309ed382
    java.util.Calendar -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToLongConverter@783071dc
    java.util.Calendar -> java.util.Date : org.springframework.format.datetime.DateFormatterRegistrar$CalendarToDateConverter@18da2904
    java.util.Date -> java.lang.Long : org.springframework.format.datetime.DateFormatterRegistrar$DateToLongConverter@3a5ea3fa
    java.uti...
复制代码
4、如果是数据校验使用校验器进行  validators
5、处理校验成功还是失败信息?BindingResult负责处理校验错误的信息
应用:
1)、自定义的类型转换器;
 

ConversionService 是 Spring 类型转换体系的核心接口。
可以利用 ConversionServiceFactoryBean 在 Spring 的 IOC 容器中定义一个 ConversionService. 
Spring 将自动识别出 IOC 容器中的 ConversionService,并在 Bean 属性配置及 
Spring MVC 处理方法入参绑定等场合使用它进行数据的转换
可通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器

复制代码
</bean>
    <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!-- 我们的类型转换器要赋值给他 -->
        <property name="converters">
        <!-- 给set赋值 -->
            <set>
                <ref bean="myStringToEmployeeConvertor"/>
            </set>
        </property>
    </bean>
    <!-- 静态资源被SpringMVC拦截到了,所以jquery 404 -->
    <!-- SpringMVC处理不了的资源,现在交给tomcat的DefaultServlet;
         default-servlet-name:
              default-servlet-name="default";如果只有它,动态资源就完蛋了;
    -->
    <!--静态资源访问ok  -->
     <mvc:default-servlet-handler/> 
         <!--动态资源就能访问;开挂版的mvc模式  -->
         <!-- 转换器采用自定义的 -->
     <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>
复制代码

1)     Spring 支持的转换器类型

Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactoryBean 中:

 Converter<S,T>:将 S 类型对象转为 T 类型对象

ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类

GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换

复制代码
public class MyStringToEmployeeConvertor implements Converter<String, Employee>{
    @Autowired
    DepartmentDao dao;
    @Override
    public Employee convert(String source) {
        // TODO Auto-generated method stub
        Employee employee = new Employee(null, "haha", "hahah", 0,null);
        System.out.println("MyStringToEmployeeConvertor开始工作啦......."+source);
        //[email protected]
        if(!"".equals(source)&&source!=null){
            //把字符串按照"-"进行分割,得到的数据保存在数组中
            String[] split = source.split("-");
            employee.setLastName(split[0]);
            employee.setEmail(split[1]);
            employee.setGender(Integer.parseInt(split[2]));
            Department department = dao.getDepartment(Integer.parseInt(split[3]));
            employee.setDepartment(department);
        }
        return employee;
    }
复制代码
JSR303数据校验;
以前没有数据校验;
          数据输错:400页面;
          我们希望:回到本页面,提示哪一项出错即可;
以后校验,在关键位置,我们都应该使用双端校验;前端+后端;
JSR303数据校验流程:
1、导包
导入校验框架的jar包; Hibernate Validator标准5个包
hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar
 Tomcat7.0以下(不包括7)需要把下面el相关的三个包的包导入到tomcat的lib里面,覆盖之前的el表达式

 

2、加注解

给需要校验的javaBean属性上加注解;给javaBean加校验注解

复制代码
    private Integer id;
    @NotEmpty
    @Length(min=5,max=15)
    private String lastName;
    @Email
    private String email;
    //1 male, 0 female
    private Integer gender=1;
    /**
     * pattern:指定日期格式
     */
    @DateTimeFormat(pattern="yyyy-MM-dd")
    @Past
    private Date birthDay;
复制代码

3、告诉SpringMVC这个javaBean需要校验;@Valid

一定注意:所有的ModelAttribute要全部统一;

* 在员工更新操作之前;在点击提交之前,提前运行查出数据
* 校验成功失败的信息,以及校验失败后怎么做?
* 给参数后面紧跟一个Errors、或者BindingResult

复制代码
public String empUpdate(@Valid @ModelAttribute("employee") Employee employee,Errors errors){
      //后端把错误显示出来 if(errors.getFieldErrorCount()>0){ System.out.println("类型转换出错误了"); //1、获取产生的所有的错误 List<FieldError> fieldErrors = errors.getFieldErrors(); for(FieldError fieldError:fieldErrors){ System.out.println(fieldError.getField()+"-"+fieldError.getDefaultMessage()); } return "/edit"; } System.out.println(employee); employeeDao.save(employee); return "redirect:/emps"; }
复制代码

4     在页面上显示错误

 Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResult 或 Errors 对象中外,还会将所有校验结果保存到“隐含模型”

即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在 “隐含对象” 中。

隐含模型中的所有数据最终将通过 HttpServletRequest 的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取错误信息

在 JSP 页面上可通过 <form:errors path=userName显示错误消息

复制代码
<form:form action="${ ctp}/emp/${employee.id }" method="post" modelAttribute="employee" >
        birthDay:<form:input path="birthDay"/><form:errors path="birthDay"/><br/>
        email:<form:input path="email"/><form:errors path="email"/><br>
        男:<form:radiobutton path="gender" value="1"/>
        女:<form:radiobutton path="gender" value="0"/><br/>
        <!-- SpringMvc习惯于直接用参数,而不是通过类。参数获取 -->
        department:<form:select path="department.id"
        items="${depts}" 
        itemLabel="departmentName"
        itemValue="id"
        ></form:select><br/>
        <!-- 浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,
        Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。
        所以要添加name="_method" value="PUT",过滤器根据这个进行区分
         -->
        <input type="hidden" name="_method" value="PUT" >
        <input type="hidden" name="id" value="${employee.id}">
        <input type="submit" value="更新">
    </form:form>
复制代码

5.    提示消息的国际化

每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的 FieldError 对象。

当一个属性校验失败后,校验框架会为该属性生成 4 个消息代码,这些代码以校验注解类名为前缀,结合 modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如 User 类中的 password 属性标注了一个 @Pattern 注解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4 个错误代码:

Pattern.user.password

Pattern.password

Pattern.java.lang.String

Pattern

当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。

若数据类型转换或数据格式转换时发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:

required:必要的参数不存在。如 @RequiredParam(“param1”) 标注了一个入参,但是该参数不存在

typeMismatch:在数据绑定时,发生数据类型不匹配的问题

methodInvocation:Spring MVC 在调用处理方法时发生了错误

 1)、创建所有错误消息提示的国际化文件

复制代码

Field error in object 'employee' on field 'lastName': rejected value [E-AA]; 
codes 
[

Length.employee.lastName,//校验注解类名.modleAttribute(保存隐含模型)的key.属性
Length.lastName,//校验注解类名.属性
Length.java.lang.String,//校验注解类名.属性类型
Length//校验注解类名

];//4种错误代码;任何字段错误都会有他的错误代码;国际化文件中的key就是要写这个错误代码;

arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employee.lastName,lastName]; arguments []; default message [lastName],15,5]; default message [length must be between 5 and 15]

复制代码
  2)、配置使用SpringMVC管理国际化资源文件
<!-- SpringMVC管理国际化资源文件 -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="error"></property>
    </bean>

3)、SpringMVC:<form:errors path="fieldName">自动的按照国际化要求取出错误信息;

 https://www.cnblogs.com/limingxian537423/p/7277538.html

https://www.jianshu.com/p/a707acd7eb3a

http://www.imooc.com/article/263093

猜你喜欢

转载自www.cnblogs.com/softidea/p/10079869.html