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);
        //admin-aaa@qq.com-0-101
        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

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM