天天看點

Spring 5 中文解析核心篇-IoC容器之資料校驗、資料綁定和類型轉換

将驗證視為業務邏輯有其優缺點,Spring提供的驗證(和資料綁定)設計不排除其中任何一種。具體來說,驗證不應與Web層綁定,并且應該易于本地化,并且應該可以插入任何可用的驗證器。考慮到這些問題,Spring提供了一個

Validator

契約,該契約既基本又可以在應用程式的每個層中使用。

資料綁定對于使使用者輸入動态綁定到應用程式的域模型(或用于處理使用者輸入的任何對象)非常有用。Spring提供了恰當地命名為

DataBinder

的功能。

Validator

DataBinder

validation

包組成,被主要的使用但不僅限于web層。

BeanWrapper

在Spring架構中是一個基本的概念并且在許多地方被使用到。然而,你大概不需要直接地使用

BeanWrapper

。但是,由于這是參考文檔,是以我們認為可能需要一些解釋。我們将在本章中解釋

BeanWrapper

,因為如果你要使用它,那麼在嘗試将資料綁定到對象時最有可能使用它。

Spring的

DataBinder

和低級别

BeanWrapper

兩者使用

PropertyEditorSupport

實作去解析和格式化屬性值。

PropertyEditor

PropertyEditorSupport

類型是JavaBeans規範的一部分并且在這個章節進行解釋。Spring 3開始引入了

core.convert

包,該包提供了正常的類型轉換工具,以及用于格式化UI字段值的進階“

format

”包。你可以将這些包用作

PropertyEditorSupport

實作的更簡單替代方案。這些也會在這個章節讨論。

Spring通過安裝基礎設計和适配Spring的

Validator

契約提供JavaBean校驗。應用程式可以全局一次啟用Bean驗證,像在 JavaBean校驗 中描述一樣,并且僅将其用于所有驗證需求。在Web層中,應用程式可以每個

DataBinder

進一步注冊控制器本地的Spring

Validator

執行個體,如配置

DataBinder

中所述,這對于插入自定義驗證邏輯很有用。

3.1 通過使用Spring的校驗接口校驗

Spring提供一個

Validator

接口,你可以使用它校驗對象。當校驗的時候,

Validator

接口通過使用Errors對象工作,是以校驗器可以報告校驗失敗資訊到

Errors

對象。

考慮下面小資料對象例子:

public class Person {

    private String name;
    private int age;

    // the usual getters and setters...
}           

下面例子通過實作下面

org.springframework.validation.Validator

接口的兩個方法為

Person

類提供校驗行為。

  • supports(Class)

    :

    Validator

    校驗接口是否支援Class
  • validate(Object, org.springframework.validation.Errors)

    : 驗證給定的對象,并在發生驗證錯誤的情況下,使用給定的

    Errors

    對象注冊這些對象。

實作

Validator

非常簡單,特别地當你知道Spring架構提供的

ValidationUtils

幫助類時。下面例子為

Person

接口實作

Validator

public class PersonValidator implements Validator {

    /**
     * This Validator validates only Person instances
     */
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }

    public void validate(Object obj, Errors e) {
        ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
        Person p = (Person) obj;
        if (p.getAge() < 0) {
            e.rejectValue("age", "negativevalue");
        } else if (p.getAge() > 110) {
            e.rejectValue("age", "too.darn.old");
        }
    }
}           

ValidationUtils

類上的靜态

rejectIfEmpty(...)

方法用于拒絕

name

屬性(如果該屬性為

null

或空字元串)。檢視

ValidationUtils

javadoc,看看它除了提供前面顯示的示例外還提供什麼功能。

雖然可以實作單個驗證器類來驗證對象中的每個嵌套對象,但更好的做法是将每個嵌套對象類的驗證邏輯封裝到自己的驗證器實作中。一個“

豐富

”對象的簡單示例是一個由兩個String屬性(第一個和第二個名字)和一個複雜的

Address

對象組成的

Customer

Address

對象可以獨立于

Customer

對象使用,是以已經實作了獨特的

AddressValidator

。如果希望

CustomerValidator

重用

AddressValidator

類中包含的邏輯而需要複制和粘貼,則可以在

CustomerValidator

中依賴注入或執行個體化一個

AddressValidator

,如以下示例所示:

public class CustomerValidator implements Validator {

    private final Validator addressValidator;

    public CustomerValidator(Validator addressValidator) {
        if (addressValidator == null) {
            throw new IllegalArgumentException("The supplied [Validator] is " +
                "required and must not be null.");
        }
        if (!addressValidator.supports(Address.class)) {
            throw new IllegalArgumentException("The supplied [Validator] must " +
                "support the validation of [Address] instances.");
        }
        this.addressValidator = addressValidator;
    }

    /**
     * This Validator validates Customer instances, and any subclasses of Customer too
     */
    public boolean supports(Class clazz) {
        return Customer.class.isAssignableFrom(clazz);
    }

    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
        Customer customer = (Customer) target;
        try {
            errors.pushNestedPath("address");
            ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
        } finally {
            errors.popNestedPath();
        }
    }
}           

驗證錯誤将報告給傳遞給驗證器的

Errors

對象。在Spring Web MVC場景中,你可以使用

<spring:bind/>

标簽去檢查錯誤資訊,但是你也可以自己檢查

Errors

對象。更多關于提供的資訊在

Javadoc

中。

參考代碼:

com.liyong.ioccontainer.service.validator.ValidatorTest

3.2 解析碼到錯誤資訊

我們介紹了資料綁定和校驗。本節介紹與驗證錯誤對應的輸出消息。在上一節顯示的例子中,我們拒絕

name

age

字段。如果我們想使用

MessageSource

去輸出錯誤資訊,我們可以使用提供的錯誤碼,當拒絕字段時(在這個場景中

name

age

)。當你

Errors

接口調用(直接地或間接地,通過使用

ValidationUtils

類)

rejectValue

或其他

reject

方法之一時,底層的實作不僅注冊你傳遞的碼,而且還注冊一些附加的錯誤碼。

MessageCodesResolver

确定哪一個錯誤碼注冊到

Errors

接口。預設情況下,使用

DefaultMessageCodesResolver

,它(例如)不僅使用你提供的代碼注冊消息,而且還注冊包含傳遞給拒絕方法的字段名稱的消息。是以,如果你通過使用

rejectValue(“age”,“too.darn.old”)

拒絕字段,則除了

too.darn.old

代碼外,Spring還将注冊

too.darn.old.age

too.darn.old.age.int

(第一個包含字段名稱,第二個包含字段類型)。這樣做是為了友善開發人員在定位錯誤消息時提供幫助。

更多

MessageCodesResolver

上和預設政策資訊可以分别地在

MessageCodesResolver

DefaultMessageCodesResolver

javadoc中找到。

3.3 bean操作和BeanWrapper

這個

org.springframework.beans

包遵循JavaBeans标準。JavaBean是具有預設無參數構造函數的類,并且遵循命名約定,在該命名約定下,例如:名為

bingoMadness

的屬性将具有

setter

方法

setBingoMadness(..)

getter

getBingoMadness()

。更多關于JavaBean資訊和規範,檢視

javaBeans

beans

包中一個非常重要的類是

BeanWrapper

接口和它的對應實作(

BeanWrapperImpl

)。就像從Javadoc引言的那樣,

BeanWrapper

提供了以下功能:設定和擷取屬性值(單獨或批量),擷取屬性描述符以及查詢屬性以确定它們是否可讀或可寫。此外,

BeanWrapper

還支援嵌套屬性,進而可以将子屬性上的屬性設定為無限深度。

BeanWrapper

還支援添加标準JavaBeans 的

PropertyChangeListeners

VetoableChangeListeners

的功能,而無需在目标類中支援代碼。最後但并非不重要的一點是,

BeanWrapper

支援設定索引屬性。

BeanWrapper

通常不直接由應用程式代碼使用,而是由

DataBinder

BeanFactory

使用。

BeanWrapper

的工作方式部分由其名稱表示:它包裝一個Bean,以對該Bean執行操作,例如設定和檢索屬性。

3.3.1 設定和擷取基本的和潛入的屬性

設定和擷取屬性是通過

BeanWrapper

的重載方法

setPropertyValue

getPropertyValue

的變體。檢視它們的詳細文檔。下面的表格顯示這些約定:

Expression Explanation

name

表示屬性

name

對應的

getName()

isName()

setName(..)

方法。

account.name

表示嵌入

account

屬性的

name

屬性對應的

getAccount().setName()

getAccount().getName()

account[2]

表示2個索引元素屬性

account

。索引屬性可以是類型

array

list

或其他自然順序集合。

account[COMPANYNAME]

表示

map

實體的值通過

account

Map屬性的key

COMPANYNAME

索引。

(如果你沒打算直接使用

BeanWrapper

,下面部分不是至關重要地。如果你僅僅使用

DataBinder

BeanFactory

和他的預設實作,你可以跳過

PropertyEditors

的部分)。

下面兩個例子類使用

BeanWrapper

去擷取和設定屬性:

public class Company {

    private String name;
    private Employee managingDirector;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Employee getManagingDirector() {
        return this.managingDirector;
    }

    public void setManagingDirector(Employee managingDirector) {
        this.managingDirector = managingDirector;
    }
}           
public class Employee {

    private String name;

    private float salary;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getSalary() {
        return salary;
    }

    public void setSalary(float salary) {
        this.salary = salary;
    }
}           

以下代碼段顯示了一些有關如何檢索和操縱執行個體化的

Company

Employee

的某些屬性的示例:

BeanWrapper company = new BeanWrapperImpl(new Company());
// setting the company name..
company.setPropertyValue("name", "Some Company Inc.");
// ... can also be done like this:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);

// ok, let's create the director and tie it to the company:
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());

// retrieving the salary of the managingDirector through the company
Float salary = (Float) company.getPropertyValue("managingDirector.salary");           
代碼示例:

com.liyong.ioccontainer.service.beanwrapper.BeanWrapperTest

3.3.2 内建

PropertyEditor

Spring使用

PropertyEditor

概念去影響一個對象和字元串之間的轉換。以不同于對象本身的方式表示屬性可能很友善。例如,日期可以用人類可讀的方式表示(如字元串:'

2007-14-09

'),而我們仍然可以将人類可讀的形式轉換回原始日期(或者更好的是,轉換任何日期以人類可讀的形式輸入到

Date

對象)。通過注冊類型為

java.beans.PropertyEditor

的自定義編輯器,可以實作此行為。在

BeanWrapper

上或在特定的IoC容器中注冊自定義編輯器(如上一章所述),使它具有如何将屬性轉換為所需類型的能力。更多關于

PropertyEditor

請參閱Oracle的java.beans包的javadoc

在Spring中使用屬性編輯的兩個示例:

  • 通過使用

    PropertyEditor

    實作在bean上設定屬性。當使用String作為在XML檔案中聲明的某個bean的屬性的值時,Spring(如果相應屬性的

    setter

    具有

    Class

    參數)将使用

    ClassEditor

    嘗試将參數解析為

    Class

  • 在Spring的MVC架構中,通過使用各種

    PropertyEditor

    實作來解析HTTP請求參數,你可以在

    CommandController

    的所有子類中手動綁定這些實作。

Spring有一個内建的

PropertyEditor

實作。它們都位于

org.springframework.beans.propertyeditors

包中。預設情況下,大多數(但不是全部,如下表所示)由

BeanWrapperImpl

注冊。如果可以通過某種方式配置屬性編輯器,則仍可以注冊自己的變體以覆寫預設變體。

下表描述了Spring提供的各種

PropertyEditor

實作:

Class

ByteArrayPropertyEditor

位元組數組的編輯器。将字元串轉換為其相應的位元組表示形式。預設

BeanWrapperImpl

注冊。

ClassEditor

将代表類的字元串解析為實際類,反之亦然。當類沒有找到抛出

IllegalArgumentException

。預設

BeanWrapperImpl

CustomBooleanEditor

Boolean

屬性的可定制屬性編輯器。預設,通過

BeanWrapperImpl

注冊,但是可以通過将其自定義執行個體注冊為自定義編輯器來覆寫它。

CustomCollectionEditor

集合屬性編輯器,轉換任何源

Collection

到給定

Collection

類型。

CustomDateEditor

java.util.Date

的可自定義屬性編輯器,支援一個自定義

DateFormat

。預設不會被注冊。必須根據需要以适當的格式進行使用者注冊。

CustomNumberEditor

任何

Number

子類可自定義屬性編輯器,例如

Integer

Long

Float

Double

。預設,通過

BeanWrapperImpl

FileEditor

解析字元串為

java.io.File

對象。預設,通過

BeanWrapperImpl

InputStreamEditor

單向屬性編輯器,它可以采用字元串并生成(通過中間的

ResourceEditor

Resource

)一個

InputStream

,以便可以将

InputStream

屬性直接設定為字元串。請注意,預設用法不會為你關閉

InputStream

。預設情況下,由

BeanWrapperImpl

注冊

LocaleEditor

可以将字元串解析為

Locale

對象,反之亦然(字元串格式為

[country] [variant]

,類似

Locale

的toString()方法相同)。預設,通過

BeanWrapperImpl

PatternEditor

能夠解析字元串為

java.util.regex.Pattern

對象,反之亦然。

PropertiesEditor

可以将字元串(格式設定為

java.util.Properties

類的javadoc中定義的格式)轉換為

Properties

對象

StringTrimmerEditor

修剪字元串的屬性編輯器。 (可選)允許将空字元串轉換為空值。預設不被注冊-必須被使用者注冊。

URLEditor

能夠轉換一個字元串代表的URL為真實的URL對象。預設,通過

BeanWrapperImpl

java.beans.PropertyEditorManager

去設定屬性編輯器可能需要的搜尋路徑。搜尋路徑也可以包含

sun.bean.editors

,它包括例如

Font

Color

和大多數原始類型的

PropertyEditor

實作。還要注意,如果标準JavaBeans基礎結構與它們處理的類在同一包中并且與該類具有相同的名稱,并且附加了

Editor

,則标準JavaBeans基礎結構會自動發現

PropertyEditor

類(無需顯式注冊它們)。例如,可以使用以下類和包結構,這就足以識别

SomethingEditor

類并将其用作某種類型屬性的

PropertyEditor

com
  chank
    pop
      Something
      SomethingEditor // SomethingEditor用作Something類           

注意,你也可以在此處使用标準的

BeanInfo

JavaBeans機制(

這裡

有所描述)。下面例子使用

BeanInfo

機制去明确地注冊一個或多個

PropertyEditor

執行個體到關聯類的屬性:

com
  chank
    pop
      Something
      SomethingBeanInfo // BeanInfo用作Something類           

下面是引用的

SomethingBeanInfo

類的Java源代碼,它将

CustomNumberEditor

Something

類的

age

屬性關聯起來:

public class SomethingBeanInfo extends SimpleBeanInfo {

    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true);
            PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Something.class) {
                public PropertyEditor createPropertyEditor(Object bean) {
                    return numberPE;
                };
            };
            return new PropertyDescriptor[] { ageDescriptor };
        }
        catch (IntrospectionException ex) {
            throw new Error(ex.toString());
        }
    }
}           

com.liyong.ioccontainer.service.propertyeditor.PropertyEditorTest

注冊附加的自定義

PropertyEditor

當設定bean屬性為字元串值時,Spring IoC容器最終地使用标準JavaBean的

PropertyEditor

實作去轉換這些字元串為屬性的複雜類型。Spring預注冊了非常多的自定義

PropertyEditor

實作(例如,将表示為字元串的類名稱轉換為Class對象)。此外,Java的标準JavaBeans

PropertyEditor

查找機制允許适當地命名類的

PropertyEditor

,并将其與提供支援的類放在同一包中,以便可以自動找到它。

如果需要注冊其他自定義

PropertyEditors

,則可以使用幾種機制。最手動的方法(通常不友善或不建議使用)是使用

ConfigurableBeanFactory

接口的

registerCustomEditor()

方法,假設你有

BeanFactory

引用。另一種(稍微友善些)的機制是使用稱為

CustomEditorConfigurer

的特殊bean工廠後處理器。盡管你可以将Bean工廠後處理器與

BeanFactory

實作一起使用,但

CustomEditorConfigurer

具有嵌套的屬性設定,是以我們強烈建議你将其與

ApplicationContext

一起使用,在這裡可以将其以與其他任何Bean相似的方式進行部署,并且可以在任何位置進行部署。自動檢測并應用。

請注意,所有的bean工廠和應用程式上下文通過使用

BeanWrapper

來處理屬性轉換,都會自動使用許多内置的屬性編輯器。上一節列出了

BeanWrapper

注冊的标準屬性編輯器。此外,

ApplicationContext

還以适合特定應用程式上下文類型的方式重寫或添加其他編輯器,以處理資源查找。

标準JavaBeans

PropertyEditor

執行個體用于将表示為字元串的屬性值轉換為屬性的實際複雜類型。你可以使用bean工廠的後處理器

CustomEditorConfigurer

來友善地将對其他

PropertyEditor

執行個體的支援添加到

ApplicationContext

考慮以下示例,該示例定義了一個名為

ExoticType

的使用者類和另一個名為

DependsOnExoticType

的類,該類需要将

ExoticType

設定為屬性:

package example;

public class ExoticType {

    private String name;

    public ExoticType(String name) {
        this.name = name;
    }
}

public class DependsOnExoticType {

    private ExoticType type;

    public void setType(ExoticType type) {
        this.type = type;
    }
}           

正确設定之後,我們希望能夠将

type

屬性配置設定為字元串,

PropertyEditor

會将其轉換為實際的

ExoticType

執行個體。以下bean定義顯示了如何建立這種關系:

<bean id="sample" class="example.DependsOnExoticType">
    <property name="type" value="aNameForExoticType"/>
</bean>           

PropertyEditor實作可能類似于以下内容:

// converts string representation to ExoticType object
package example;

public class ExoticTypeEditor extends PropertyEditorSupport {

    public void setAsText(String text) {
        setValue(new ExoticType(text.toUpperCase()));
    }
}           

最後,下面的示例示範如何使用

CustomEditorConfigurer

ApplicationContext

注冊新的

PropertyEditor

,然後可以根據需要使用它:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
        </map>
    </property>
</bean>           

com.liyong.ioccontainer.starter.PropertyEditorIocContainer

使用PropertyEditorRegistrar

在Spring容器中注冊屬性編輯器的其他機制是建立和使用

PropertyEditorRegistrar

。當需要在幾種不同情況下使用同一組屬性編輯器時,此接口特别有用。你可以在每一種場景中寫對應的注冊和重新使用。

PropertyEditorRegistrar

執行個體與一個名為

PropertyEditorRegistry

的接口一起工作,該接口由Spring

BeanWrapper

(和

DataBinder

)實作。與

CustomEditorConfigurer

(在此描述)結合使用時,

PropertyEditorRegistrar

執行個體特别友善,該執行個體暴露了名為

setPropertyEditorRegistrars(..)

的屬性。以這種方式添加到

CustomEditorConfigurer

中的

PropertyEditorRegistrar

執行個體可以輕松地與

DataBinder

和Spring MVC控制器共享。此外,它避免了在自定義編輯器上進行同步的需求:希望

PropertyEditorRegistrar

為每次建立bean的嘗試建立新的

PropertyEditor

執行個體。

以下示例說明如何建立自己的

PropertyEditorRegistrar

package com.foo.editors.spring;

public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {

    public void registerCustomEditors(PropertyEditorRegistry registry) {

        // 期望建立一個新的PropertyEditor示例
        registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());

        // you could register as many custom property editors as are required here...
    }
}           

另請參閱

org.springframework.beans.support.ResourceEditorRegistrar

以擷取示例

PropertyEditorRegistrar

實作。注意,在實作

registerCustomEditors(...)

方法時,它如何建立每個屬性編輯器的新執行個體。

下一個示例顯示了如何配置

CustomEditorConfigurer

并将其注入我們的

CustomPropertyEditorRegistrar

的執行個體:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <list>
            <ref bean="customPropertyEditorRegistrar"/>
        </list>
    </property>
</bean>

<bean id="customPropertyEditorRegistrar"
    class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>           

最後(對于使用Spring的MVC Web架構的讀者來說,與本章的重點有所偏離),使用

PropertyEditorRegistrars

與資料綁定

Controllers

(例如

SimpleFormController

)結合使用會非常友善。下面的示例在

initBinder(..)

方法的實作中使用

PropertyEditorRegistrar

public final class RegisterUserController extends SimpleFormController {

    private final PropertyEditorRegistrar customPropertyEditorRegistrar;

    public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
        this.customPropertyEditorRegistrar = propertyEditorRegistrar;
    }

    protected void initBinder(HttpServletRequest request,
            ServletRequestDataBinder binder) throws Exception {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder);
    }

    // other methods to do with registering a User
}           

這種

PropertyEditor

注冊樣式可以使代碼簡潔(

initBinder(..)

的實作隻有一行長),并且可以将通用的

PropertyEditor

注冊代碼封裝在一個類中,然後根據需要在許多

Controller

之間共享。

3.4 Spring類型轉換

Spring 3 已經引入一個

core.convert

包,它提供了一般類型系統轉換。系統定義了一個用于實作類型轉換邏輯的SPI和一個用于在運作時執行類型轉換的API。在Spring容器中,可以使用此特性作為

PropertyEditor

實作的替代方法,以将外部化的bean屬性值字元串轉換為所需的屬性類型。你還可以在應用程式中需要類型轉換的任何地方使用公共API。

3.4.1 轉換SPI

如以下接口定義所示,用于實作類型轉換邏輯的SPI非常簡單且具有強類型:

package org.springframework.core.convert.converter;

public interface Converter<S, T> {

   T convert(S source);
}           

要建立自己的轉換器,請實作

Converter

接口,并将

S

設定為要被轉換的類型,并将

T

設定為要轉換為的類型。如果需要将

S

的集合或數組轉換為

T

的集合,并且已經注冊了委托數組或集合轉換器(預設情況下,

DefaultConversionService

會這樣做),那麼你還可以透明地應用這樣的轉換器。

對于每次

convert(S)

的調用,方法參數必須保證不能為

null

。如果轉換失敗,你的

Converter

可能抛出未檢查異常。特别地,它可能抛出

IllegalArgumentException

去報告無效參數值異常。小心的去確定

Converter

實作是線程安全的。

為了友善起見,在

core.convert.support

包中提供了幾種轉換器實作。這些包括從字元串到數字和其他常見類型的轉換器。下面的清單顯示了

StringToInteger

類,它是一個典型的

Converter

package org.springframework.core.convert.support;

final class StringToInteger implements Converter<String, Integer> {

    public Integer convert(String source) {
        return Integer.valueOf(source);
    }
}           

3.4.2 使用

ConverterFactory

當需要集中整個類層次結構的轉換邏輯時(例如,從

String

轉換為

Enum

對象時),可以實作

ConverterFactory

package org.springframework.core.convert.converter;

public interface ConverterFactory<S, R> {

    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}           

參數化

S

為你要轉換的類型,參數

R

為基礎類型,定義可以轉換為的類的範圍。然後實作

getConverter(Class <T>)

,其中

T

R

的子類。

考慮

StringToEnumConverterFactory

例子:

package org.springframework.core.convert.support;

final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnumConverter(targetType);
    }

    private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {

        private Class<T> enumType;

        public StringToEnumConverter(Class<T> enumType) {
            this.enumType = enumType;
        }

        public T convert(String source) {
            return (T) Enum.valueOf(this.enumType, source.trim());
        }
    }
}           

3.4.3 使用

GenericConverter

當你需要複雜的

Converter

實作時,請考慮使用

GenericConverter

接口。與

Converter

相比,

GenericConverter

具有比

Converter

更靈活但類型不強的簽名,支援多種源類型和目标類型之間進行轉換。此外,

GenericConverter

還提供了在實作轉換邏輯時可以使用的源和目标字段上下文。這樣的上下文允許由字段注解或在字段簽名上聲明的泛型資訊驅動類型轉換。下面清單顯示

GenericConverter

接口定義:

package org.springframework.core.convert.converter;

public interface GenericConverter {

    public Set<ConvertiblePair> getConvertibleTypes();

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}           

GenericConverter

,需要

getConvertibleTypes()

傳回支援的源→目标類型對。然後實作

convert(Object, TypeDescriptor, TypeDescriptor)

去包含你的轉換邏輯。源

TypeDescriptor

提供對包含正在轉換的值的源字段的通路。使用目标

TypeDescriptor

,可以通路要設定轉換值的目标字段。

GenericConverter

的一個很好的例子是在Java數組和集合之間進行轉換的轉換器。這樣的

ArrayToCollectionConverter

會檢查聲明目标集合類型的字段以解析集合的元素類型。這樣就可以在将集合設定到目标字段上之前,将源數組中的每個元素轉換為集合元素類型。

由于

GenericConverter

是一個更複雜的SPI接口,是以僅應在需要時使用它。支援

Converter

ConverterFactory

以滿足基本的類型轉換需求。

com.liyong.ioccontainer.service.converter.GenericConverterTest

使用

ConditionalGenericConverter

有時,你希望

Converter

僅在滿足特定條件時才運作。例如,你可能隻想在目标字段上存在特定注解時才運作

Converter

,或者可能在目标類上定義了特定方法(例如靜态

valueOf

方法)時才運作

Converter

ConditionalGenericConverter

GenericConverter

ConditionalConverter

接口的聯合,可讓你定義以下自定義比對條件:

public interface ConditionalConverter {

    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}           

ConditionalGenericConverter

的一個很好的例子是

EntityConverter

,它在持久實體辨別和實體引用之間轉換。僅當目标實體類型聲明靜态查找器方法(例如

findAccount(Long)

)時,此類

EntityConverter

才可能比對。你可以在

matchs(TypeDescriptor,TypeDescriptor)

的實作中執行這種

finder

方法檢查。

com.liyong.ioccontainer.service.converter.ConditionalConverterTest

3.4.4

ConversionService

API

ConversionService

定義了一個統一的API,用于在運作時執行類型轉換邏輯。轉換器通常在以下門面接口執行:

package org.springframework.core.convert;

public interface ConversionService {

    boolean canConvert(Class<?> sourceType, Class<?> targetType);

    <T> T convert(Object source, Class<T> targetType);

    boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}           

大多數

ConversionService

實作也都實作

ConverterRegistry

,該轉換器提供用于注冊轉換器的SPI。在内部,

ConversionService

實作委派其注冊的轉換器執行類型轉換邏輯。

core.convert.support

包中提供了一個強大的

ConversionService

實作。

GenericConversionService

是适用于大多數環境的通用實作。

ConversionServiceFactory

提供了一個友善的工廠來建立通用的ConversionService配置。

3.4.5 配置

ConversionService

ConversionService

是無狀态對象,旨在在應用程式啟動時執行個體化,然後在多個線程之間共享。在Spring應用程式中,通常為每個Spring容器(或

ApplicationContext

)配置一個

ConversionService

執行個體。當架構需要執行類型轉換時,Spring會使用該

ConversionService

并使用它。你還可以将此

ConversionService

注入到任何bean中,然後直接調用它。

如果沒有向Spring注冊

ConversionService

,則使用原始的基于

propertyeditor

的特性。

要向Spring注冊預設的

ConversionService

,請添加以下bean定義,其id為

conversionService

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

預設的

ConversionService

可以在字元串、數字、枚舉、集合、映射和其他常見類型之間進行轉換。要用你自己的自定義轉換器補充或覆寫預設轉換器,請設定

converters

屬性。屬性值可以實作

Converter

ConverterFactory

GenericConverter

接口中的任何一個。

<bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>           

在Spring MVC應用程式中使用

ConversionService

也很常見。參見Spring MVC一章中的

轉換和格式化

在某些情況下,你可能希望在轉換過程中應用格式設定。有關使用

FormattingConversionServiceFactoryBean

的詳細資訊,請參見

FormatterRegistry SPI

3.4.6 程式設計式地使用

ConversionService

要以程式設計方式使用

ConversionService

執行個體,可以像對其他任何bean一樣注入對該bean例的引用。以下示例顯示了如何執行此操作:

@Service
public class MyService {

    public MyService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public void doIt() {
        this.conversionService.convert(...)
    }
}           

對于大多數用例,可以使用指定

targetType

convert

方法,但不适用于更複雜的類型,例如參數化元素的集合。例如,如果要以程式設計方式将整數清單轉換為字元串清單,則需要提供源類型和目标類型的格式定義。

幸運的是,如下面的示例所示,

TypeDescriptor

提供了各種選項來使操作變得簡單明了:

DefaultConversionService cs = new DefaultConversionService();

List<Integer> input = ...
cs.convert(input,
    TypeDescriptor.forObject(input), // List<Integer> type descriptor
    TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));           

請注意,

DefaultConversionService

自動注冊适用于大多數環境的轉換器。這包括集合轉換器、标量轉換器和基本的對象到字元串轉換器。你可以使用

DefaultConversionService

addDefaultConverters

方法向任何

ConverterRegistry

注冊相同的轉換器。

值類型的轉換器可重用于數組和集合,是以,假設标準集合處理适當,則無需建立特定的轉換器即可将

S

的集合轉換為

T

的集合。

3.5 Spring字段格式

如上一節所述,

core.convert

是一種通用類型轉換系統。它提供了統一的

ConversionService

API和強類型的

Converter

SPI,用于實作從一種類型到另一種類型的轉換邏輯。Spring容器使用此系統綁定bean屬性值。此外,Spring Expression Language(SpEL)和

DataBinder

都使用此系統綁定字段值。例如,當SpEL需要強制将

Short

Long

來完成

expression.setValue(Object bean,Object value)

嘗試時,

core.convert

系統将執行強制轉換。

考慮一個典型的用戶端環境轉換需求,例如web或桌面應用。在這種環境中,你通常将字元串轉換為支援用戶端送出處理,以及将字元串轉換為支援視圖呈現過程。以及,你通常需要本地化

String

值。更通用的

core.convert

Converter

SPI不能直接滿足此類格式化要求。為了直接解決這些問題,Spring 3 引入了友善的

Formatter

SPI,它為用戶端環境提供了

PropertyEditor

實作的簡單而強大的替代方案。

通常,當你需要實作通用類型轉換邏輯時,可以使用

Converter

SPI,例如,在

java.util.Date

Long

之間轉換。當你在用戶端環境中(例如,web應用)并且需要去解析和列印本地化字段值時,你可以使用

Formatter

SPI。

ConversionService

為這兩個SPI提供統一的類型轉換。

3.5.1

Formatter

SPI

Formatter

SPI去實作字段格式邏輯是簡單和強類型的。下面清單顯示

Formatter

接口資訊:

package org.springframework.format;

public interface Formatter<T> extends Printer<T>, Parser<T> {
}           

Formatter

Printer

Parser

建構塊接口拓展。下面清單顯示這兩個接口定義:

public interface Printer<T> {

    String print(T fieldValue, Locale locale);
}           
import java.text.ParseException;

public interface Parser<T> {

    T parse(String clientValue, Locale locale) throws ParseException;
}           

去建立你自己的

Formatter

,實作前面展示的

Formatter

接口。将

T

參數化為你希望格式化的對象類型-例如,

java.util.Date

。實作

print()

操作以列印

T

的執行個體以在用戶端語言環境中顯示。實作

parse()

操作,以從用戶端本地傳回的格式化表示形式解析

T

的執行個體。如果嘗試解析失敗,你的

Formatter

應該抛一個

ParseException

IllegalArgumentException

異常。注意確定你的

Formatter

為了友善

format

子包提供一些

Formatter

number

包提供

NumberStyleFormatter

CurrencyStyleFormatter

PercentStyleFormatter

去格式化

Number

對象,它使用

java.text.NumberFormat

datetime

DateFormatter

java.util.Date

java.text.DateFormat

datetime.joda

包基于

Joda-Time

庫提供了全面的日期時間格式支援。

下面

DateFormatter

Formatter

實作例子:

package org.springframework.format.datetime;

public final class DateFormatter implements Formatter<Date> {

    private String pattern;

    public DateFormatter(String pattern) {
        this.pattern = pattern;
    }

    public String print(Date date, Locale locale) {
        if (date == null) {
            return "";
        }
        return getDateFormat(locale).format(date);
    }

    public Date parse(String formatted, Locale locale) throws ParseException {
        if (formatted.length() == 0) {
            return null;
        }
        return getDateFormat(locale).parse(formatted);
    }

    protected DateFormat getDateFormat(Locale locale) {
        DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
        dateFormat.setLenient(false);
        return dateFormat;
    }
}           

Spring歡迎社群驅動

Formatter

貢獻。檢視

GitHub Issues

去貢獻。

3.5.2 注解驅動格式

可以通過字段類型或注解配置字段格式。要将注解綁定到

Formatter

,請實作

AnnotationFormatterFactory

。下面清單顯示

AnnotationFormatterFactory

package org.springframework.format;

public interface AnnotationFormatterFactory<A extends Annotation> {

    Set<Class<?>> getFieldTypes();

    Printer<?> getPrinter(A annotation, Class<?> fieldType);

    Parser<?> getParser(A annotation, Class<?> fieldType);
}           

去建立一個實作:将

A

參數化為要與格式邏輯關聯的字段

annotationType

,例如,

org.springframework.format.annotation.DateTimeFormat

getFieldTypes()

傳回可在其上使用注解的字段類型。讓

getPrinter()

傳回

Printer

以列印帶注解的字段的值。讓

getParser()

Parser

去為注解字段解析

clientValue

下面的示例

AnnotationFormatterFactory

實作将

@NumberFormat

注解綁定到格式化程式,以指定數字樣式或模式:

public final class NumberFormatAnnotationFormatterFactory
        implements AnnotationFormatterFactory<NumberFormat> {

    public Set<Class<?>> getFieldTypes() {
        return new HashSet<Class<?>>(asList(new Class<?>[] {
            Short.class, Integer.class, Long.class, Float.class,
            Double.class, BigDecimal.class, BigInteger.class }));
    }

    public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) {
        return configureFormatterFrom(annotation, fieldType);
    }

    public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) {
        return configureFormatterFrom(annotation, fieldType);
    }

    private Formatter<Number> configureFormatterFrom(NumberFormat annotation, Class<?> fieldType) {
        if (!annotation.pattern().isEmpty()) {
            return new NumberStyleFormatter(annotation.pattern());
        } else {
            Style style = annotation.style();
            if (style == Style.PERCENT) {
                return new PercentStyleFormatter();
            } else if (style == Style.CURRENCY) {
                return new CurrencyStyleFormatter();
            } else {
                return new NumberStyleFormatter();
            }
        }
    }
}           

觸發格式,可以使用

@NumberFormat

注解字段,如以下示例所示:

public class MyModel {
    @NumberFormat(style=Style.CURRENCY)
    private BigDecimal decimal;
}           

格式注解API

org.springframework.format.annotation

包中存在一個可移植的格式注解API。你可以使用

@NumberFormat

格式化

Number

字段(例如

Double

Long

),并使用

@DateTimeFormat

java.util.Date

java.util.Calendar

Long

(用于毫秒時間戳)以及JSR-310

java.time

Joda-Time

值類型。

下面例子使用

@DateTimeFormat

去格式

java.util.Date

為ISO日期(

yyyy-MM-dd

);

public class MyModel {

    @DateTimeFormat(iso=ISO.DATE)
    private Date date;
}           

3.5.3

FormatterRegistry

FormatterRegistry

是一個SPI用于注冊格式化器和轉換器。

FormattingConversionService

FormatterRegistry

實作适用于絕大環境。通過使用

FormattingConversionServiceFactoryBean

,你可以程式設計式地或聲明式配置這些變體作為Spring bean。由于此實作還實作了

ConversionService

,是以你可以直接将其配置為與Spring的

DataBinder

和Spring表達式語言(SpEL)一起使用。

下面清單顯示

FormatterRegistry

SPI接口定義:

package org.springframework.format;

public interface FormatterRegistry extends ConverterRegistry {

    void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser);

    void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);

    void addFormatterForFieldType(Formatter<?> formatter);

    void addFormatterForAnnotation(AnnotationFormatterFactory<?> factory);
}           

像在前面清單顯示,你通過字段類型或通過注解注冊格式化器。

FormatterRegistry

SPI使你可以集中配置格式設定規則,而不必在控制器之間重複此類配置。例如,你可能要強制所有日期字段以某種方式設定格式或帶有特定注解的字段以某種方式設定格式。使用共享的

FormatterRegistry

,你可以一次定義這些規則,并在需要格式化時應用它們。

3.5.4

FormatterRegistrar

FormatterRegistrar

是一個SPI,用于通過

FormatterRegistry

注冊格式器和轉換器。以下清單顯示了其接口定義:

package org.springframework.format;

public interface FormatterRegistrar {

    void registerFormatters(FormatterRegistry registry);
}           

為給定的格式類别(例如日期格式)注冊多個相關的轉換器和格式器時,

FormatterRegistrar

很有用。在聲明式注冊不充分的情況下它也很有用。例如,當格式化程式需要在不同于其自身的特定字段類型下進行索引時,或者在注冊

Printer

/

Parser

對時。下一節将提供有關轉換器和格式化注冊的更多資訊。

3.5.5 在Spring MVC中配置格式化

在Spring MVC章節中,檢視

Conversion 和 Formatting
3.6 配置全局

Date

Time

格式

預設情況下,未使用

@DateTimeFormat

注解日期和時間字段是使用

DateFormat.SHORT

格式從字元串轉換的。如果願意,可以通過定義自己的全局格式來更改此設定。

為此,請確定Spring不注冊預設格式器。相反,可以借助以下方法手動注冊格式化器:

  • org.springframework.format.datetime.standard.DateTimeFormatterRegistrar

  • org.springframework.format.datetime.DateFormatterRegistrar

    或為

    Joda-Time

    org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar

例如,下面Java配置注冊一個全局的

yyyyMMdd

格式:

@Configuration
public class AppConfig {

    @Bean
    public FormattingConversionService conversionService() {

        // Use the DefaultFormattingConversionService but do not register defaults
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);

        // Ensure @NumberFormat is still supported
        conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());

        // Register JSR-310 date conversion with a specific global format
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
        registrar.registerFormatters(conversionService);

        // Register date conversion with a specific global format
        DateFormatterRegistrar registrar = new DateFormatterRegistrar();
        registrar.setFormatter(new DateFormatter("yyyyMMdd"));
        registrar.registerFormatters(conversionService);

        return conversionService;
    }
}           

如果你偏好與基于XML配置,你可以使用

FormattingConversionServiceFactoryBean

。下面例子顯示怎樣去做(這裡使用

Joda Time

):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd>

    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="registerDefaultFormatters" value="false" />
        <property name="formatters">
            <set>
                <bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
            </set>
        </property>
        <property name="formatterRegistrars">
            <set>
                <bean class="org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar">
                    <property name="dateFormatter">
                        <bean class="org.springframework.format.datetime.joda.DateTimeFormatterFactoryBean">
                            <property name="pattern" value="yyyyMMdd"/>
                        </bean>
                    </property>
                </bean>
            </set>
        </property>
    </bean>
</beans>           

注意:當在web應用中配置日期和時間格式時需要額外考慮。請檢視

WebMVC Conversion 和 Formatting

or

WebFlux Conversion 和 Formatting

.

3.7 Java Bean校驗

Spring架構提供對J

ava Bean

校驗API。

3.7.1 Bean校驗概要

Bean驗證為Java應用程式提供了通過限制聲明和中繼資料進行驗證的通用方法。要使用它,你需要使用聲明性驗證限制對域模型屬性進行注解,然後由通過運作時強制實施限制。有内置的限制,你也可以定義自己的自定義限制。

考慮以下示例,該示例顯示了具有兩個屬性的簡單

PersonForm

模型:

public class PersonForm {
    private String name;
    private int age;
}           

Bean驗證使你可以聲明限制,如以下示例所示:

public class PersonForm {

    @NotNull
    @Size(max=64)
    private String name;

    @Min(0)
    private int age;
}           

然後,Bean驗證器根據聲明的限制來驗證此類的執行個體。有關該API的一般資訊,請參見

Bean Validation

。有關特定限制,請參見

Hibernate Validator

文檔。要學習如何将bean驗證提供程式設定為Spring bean,請繼續閱讀。

3.7.2 配置Bean Validation提供者

Spring提供了對Bean驗證API的全面支援,包括将Bean驗證提供程式作為Spring Bean執行引導。這使你可以在應用程式中需要驗證的任何地方注入

javax.validation.ValidatorFactory

javax.validation.Validator

你可以使用

LocalValidatorFactoryBean

将預設的

Validator

配置為Spring Bean,如以下示例所示:

import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class AppConfig {

    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean;
    }
}           

前面示例中的基本配置觸發Bean驗證以使用其預設引導機制進行初始化。Bean驗證提供程式,例如

Hibernate

Validator

,應該存在于類路徑中并被自動檢測到。

注入校驗器

LocalValidatorFactoryBean

同時實作

javax.validation.ValidatorFactory

javax.validation.Validator

以及Spring的

org.springframework.validation.Validator

。你可以将對這些接口之一的引用注入需要調用驗證邏輯的bean中。

如果你希望直接使用Bean

Validation

API,則可以注入對

javax.validation.Validator

的引用,如以下示例所示:

import javax.validation.Validator;

@Service
public class MyService {

    @Autowired
    private Validator validator;
}           

配置自定義限制

每個bean校驗限制由兩部分組成:

  • @Constraint

    注解,用于聲明限制及其可配置屬性。
  • javax.validation.ConstraintValidator

    接口的實作,用于實作限制的行為。

要将聲明與實作相關聯,每個

@Constraint

注解都引用一個對應的

ConstraintValidator

實作類。在運作時,當在域模型中遇到限制注解時,

ConstraintValidatorFactory

執行個體化引用的實作。

預設情況下,

LocalValidatorFactoryBean

配置一個

SpringConstraintValidatorFactory

,該工廠使用Spring建立

ConstraintValidator

執行個體。這使你的自定義

ConstraintValidators

像其他任何Spring bean一樣受益于依賴項注入。

以下示例顯示了一個自定義

@Constraint

聲明,後跟一個關聯的

ConstraintValidator

實作,該實作使用Spring進行依賴項注入:

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint {
}           
import javax.validation.ConstraintValidator;

public class MyConstraintValidator implements ConstraintValidator {

    @Autowired;
    private Foo aDependency;

    // ...
}           

如前面的示例所示,

ConstraintValidator

實作可以像其他任何Spring bean一樣具有

@Autowired

依賴項。

com.liyong.ioccontainer.service.validator.ConstraintTest

Spring驅動方法驗證

你可以通過

MethodValidationPostProcessor

bean定義将

Bean Validation 1.1

(以及作為自定義擴充,還包括

Hibernate

Validator 4.3

)支援的方法驗證功能內建到Spring上下文中:

import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

@Configuration

public class AppConfig {

    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
        return new MethodValidationPostProcessor;
    }
}           

為了有資格進行Spring驅動的方法驗證,所有目标類都必須使用Spring的

@Validated

注解進行注釋,該注解也可以選擇聲明要使用的驗證組。有關使用

Hibernate Validator

Bean Validation 1.1

提供程式的設定詳細資訊,請參見

MethodValidationPostProcessor
方法驗證依賴于目标類周圍的AOP代理,即接口上方法的JDK動态代理或CGLIB代理。代理的使用存在某些限制,在 了解 AOP 代理 中介紹了其中的一些限制。另外,請記住在代理類上使用方法和通路器;直接通路将不起作用。

com.liyong.ioccontainer.starter.MethodvalidationIocContainer

其他配置選項

在大多數情況下,預設

LocalValidatorFactoryBean

配置就足夠了。從消息插值到周遊解析,有多種用于各種Bean驗證構造的配置選項。有關這些選項的更多資訊,請參見

LocalValidatorFactoryBean

Javadoc。

3.7.3 配置

DataBinder

從Spring 3 開始,你可以使用

Validator

配置

DataBinder

執行個體。配置完成後,你可以通過調用

binder.validate()

來調用

Validator

。任何驗證錯誤都會自動添加到綁定的

BindingResult

下面的示例示範如何在綁定到目标對象後,以程式設計方式使用

DataBinder

來調用驗證邏輯:

Foo target = new Foo();
DataBinder binder = new DataBinder(target);
binder.setValidator(new FooValidator());

// bind to the target object
binder.bind(propertyValues);

// validate the target object
binder.validate();

// get BindingResult that includes any validation errors
BindingResult results = binder.getBindingResult();           

你還可以通過

dataBinder.addValidators

dataBinder.replaceValidators

配置具有多個

Validator

執行個體的

DataBinder

。當将全局配置的bean驗證與在

DataBinder

執行個體上本地配置的Spring

Validator

結合使用時,這很有用。檢視

Spring MVC 校驗配置

com.liyong.ioccontainer.service.validator.ValidatorTest

3.7.4 Spring MVC 3 校驗

在Sprint MVC 章節中,檢視

Validation

作者

個人從事金融行業,就職過易極付、思建科技、某網約車平台等重慶一流技術團隊,目前就職于某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大資料、資料存儲、自動化內建和部署、分布式微服務、響應式程式設計、人工智能等領域。同時也熱衷于技術分享創立公衆号和部落格站點對知識體系進行分享。關注公衆号:青年IT男 擷取最新技術文章推送!

部落格位址:

http://youngitman.tech

CSDN:

https://blog.csdn.net/liyong1028826685

微信公衆号:

Spring 5 中文解析核心篇-IoC容器之資料校驗、資料綁定和類型轉換

技術交流群:

Spring 5 中文解析核心篇-IoC容器之資料校驗、資料綁定和類型轉換