成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【小家Spring】聊聊Spring中的數(shù)據(jù)綁定 --- DataBinder本尊(源碼分析)

charles_paul / 3569人閱讀

摘要:對(duì)中的數(shù)據(jù)綁定場景,小伙伴們就再熟悉不過了。比如包下大名鼎鼎的源碼分析的源碼相對(duì)來說還是頗為復(fù)雜的,它提供的能力非常強(qiáng)大,也注定了它的方法非常多屬性也非常多。并且備注入群字樣,會(huì)手動(dòng)邀請(qǐng)入群

每篇一句
唯有熱愛和堅(jiān)持,才能讓你在程序人生中屹立不倒,切忌跟風(fēng)什么語言或就學(xué)什么去~
相關(guān)閱讀

【小家Spring】聊聊Spring中的數(shù)據(jù)綁定 --- 屬性訪問器PropertyAccessor和實(shí)現(xiàn)類DirectFieldAccessor的使用
【小家Spring】聊聊Spring中的數(shù)據(jù)綁定 --- BeanWrapper以及Java內(nèi)省Introspector和PropertyDescriptor

對(duì)Spring感興趣可掃碼加入wx群:Java高工、架構(gòu)師3群(文末有二維碼)

前言

數(shù)據(jù)綁定 這個(gè)概念在任何一個(gè)成型的框架中都是特別重要的(尤其是web框架),它能讓框架更多的自動(dòng)化,更好容錯(cuò)性以及更高的編碼效率。它提供的能力是:把字符串形式的參數(shù)轉(zhuǎn)換成服務(wù)端真正需要的類型的轉(zhuǎn)換(當(dāng)然可能還包含校驗(yàn))。

對(duì)Spring中的數(shù)據(jù)綁定場景,小伙伴們就再熟悉不過了。比如我們Controller中只需要使用Model對(duì)象就能完成request到Model對(duì)象的自動(dòng)數(shù)據(jù)自動(dòng)綁定,使用起來著實(shí)非常方便~(完全屏蔽了Servlet的API

既然數(shù)據(jù)綁定這么重要,但是為何鮮有人提起呢?我也上網(wǎng)搜了搜關(guān)于DataBinder的相關(guān)資料,相對(duì)來說還是寥寥無幾的~
我們不提起并不代表它不重要,這些都是Spring它幫我們默默的干了。這也印證了那句名言嘛:我們的安好是因?yàn)橛腥颂嫖覀冐?fù)重前行

查到網(wǎng)上的資料,大都停留在如何使用WebDataBinder的說明上,并且?guī)缀鯖]有文章是專門分析核心部件DataBinder的,本文作為此方面的一股清流,在此把我結(jié)合官方文檔、源碼的所獲分享給大家~

DataBinder

注意,此類所在的包是org.springframework.validation,所以可想而知,它不僅僅完成數(shù)據(jù)的綁定,還會(huì)和數(shù)據(jù)校驗(yàn)有關(guān)~

注意:我看到有的文章說DataBinder在綁定的時(shí)候還會(huì)進(jìn)行數(shù)據(jù)校驗(yàn)Validation,其實(shí)這個(gè)是不準(zhǔn)確的,容易誤導(dǎo)人(校驗(yàn)動(dòng)作不發(fā)生在DataBinder本類)

還有說DataBinder數(shù)據(jù)綁定最終依賴的是BeanWrapper,其實(shí)這個(gè)也是不準(zhǔn)確的,實(shí)際上依賴的是PropertyAccessor

DataBinder使用Demo

先看一個(gè)簡單Demo,體驗(yàn)一把直接使用DataBinder進(jìn)行數(shù)據(jù)綁定吧:

    public static void main(String[] args) throws BindException {
        Person person = new Person();
        DataBinder binder = new DataBinder(person, "person");
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.add("name", "fsx");
        pvs.add("age", 18);

        binder.bind(pvs);
        Map close = binder.close();

        System.out.println(person);
        System.out.println(close);
    }

輸出:

Person{name="fsx", age=18}
{person=Person{name="fsx", age=18}, org.springframework.validation.BindingResult.person=org.springframework.validation.BeanPropertyBindingResult: 0 errors}

其實(shí)Spring一直是在弱化數(shù)據(jù)綁定對(duì)使用者的接觸(這就是為何鮮有人提起的原因),所以之前博文也說到Spring并不推薦直接使用BeanWrapper去自己綁定數(shù)據(jù)(而是都讓框架自己來完成吧~)。

BeanWrapper不推薦直接使用,但是DataBinder是一個(gè)更為成熟、完整些的數(shù)據(jù)綁定器,若實(shí)在有需求使用它是比使用BeanWrapper是個(gè)更好的選擇~

其實(shí)直接使用頂層的DataBinder也是一般不會(huì)的,而是使用它的子類。比如web包下大名鼎鼎的WebDataBinder~
源碼分析

DataBinder的源碼相對(duì)來說還是頗為復(fù)雜的,它提供的能力非常強(qiáng)大,也注定了它的方法非常多、屬性也非常多。
首先看看類聲明:

public class DataBinder implements PropertyEditorRegistry, TypeConverter {}

它是個(gè)實(shí)現(xiàn)類,直接實(shí)現(xiàn)了PropertyEditorRegistryTypeConverter 這兩個(gè)接口,因此它可以注冊(cè)java.beans.PropertyEditor,并且能完成類型轉(zhuǎn)換(TypeConverter)。

關(guān)于數(shù)據(jù)轉(zhuǎn)換這塊內(nèi)容,有興趣的可參見:【小家Spring】聊聊Spring中的數(shù)據(jù)轉(zhuǎn)換:Converter、ConversionService、TypeConverter、PropertyEditor

接下里分析具體源碼(需要解釋說明都寫在源碼處了):

public class DataBinder implements PropertyEditorRegistry, TypeConverter {

    /** Default object name used for binding: "target". */
    public static final String DEFAULT_OBJECT_NAME = "target";
    /** Default limit for array and collection growing: 256. */
    public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256;

    @Nullable
    private final Object target;
    private final String objectName; // 默認(rèn)值是target

    // BindingResult:綁定錯(cuò)誤、失敗的時(shí)候會(huì)放進(jìn)這里來~
    @Nullable
    private AbstractPropertyBindingResult bindingResult;

    //類型轉(zhuǎn)換器,會(huì)注冊(cè)最為常用的那么多類型轉(zhuǎn)換Map, PropertyEditor> defaultEditors
    @Nullable
    private SimpleTypeConverter typeConverter;

    // 默認(rèn)忽略不能識(shí)別的字段~
    private boolean ignoreUnknownFields = true;
    // 不能忽略非法的字段(比如我要Integer,你給傳aaa,那肯定就不讓綁定了,拋錯(cuò))
    private boolean ignoreInvalidFields = false;
    // 默認(rèn)是支持級(jí)聯(lián)的~~~
    private boolean autoGrowNestedPaths = true;

    private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT;

    // 這三個(gè)參數(shù)  都可以自己指定~~ 允許的字段、不允許的、必須的
    @Nullable
    private String[] allowedFields;
    @Nullable
    private String[] disallowedFields;
    @Nullable
    private String[] requiredFields;

    // 轉(zhuǎn)換器ConversionService
    @Nullable
    private ConversionService conversionService;
    // 狀態(tài)碼處理器~
    @Nullable
    private MessageCodesResolver messageCodesResolver;
    // 綁定出現(xiàn)錯(cuò)誤的處理器~
    private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
    // 校驗(yàn)器(這個(gè)非常重要)
    private final List validators = new ArrayList<>();

    //  objectName沒有指定,就用默認(rèn)的
    public DataBinder(@Nullable Object target) {
        this(target, DEFAULT_OBJECT_NAME);
    }
    public DataBinder(@Nullable Object target, String objectName) {
        this.target = ObjectUtils.unwrapOptional(target);
        this.objectName = objectName;
    }
    ... // 省略所有屬性的get/set方法

    // 提供一些列的初始化方法,供給子類使用 或者外部使用  下面兩個(gè)初始化方法是互斥的
    public void initBeanPropertyAccess() {
        Assert.state(this.bindingResult == null, "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
        this.bindingResult = createBeanPropertyBindingResult();
    }
    protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
        BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
        if (this.conversionService != null) {
            result.initConversion(this.conversionService);
        }
        if (this.messageCodesResolver != null) {
            result.setMessageCodesResolver(this.messageCodesResolver);
        }
        return result;
    }
    // 你會(huì)發(fā)現(xiàn),初始化DirectFieldAccess的時(shí)候,校驗(yàn)的也是bindingResult ~~~~
    public void initDirectFieldAccess() {
        Assert.state(this.bindingResult == null, "DataBinder is already initialized - call initDirectFieldAccess before other configuration methods");
        this.bindingResult = createDirectFieldBindingResult();
    }
    protected AbstractPropertyBindingResult createDirectFieldBindingResult() {
        DirectFieldBindingResult result = new DirectFieldBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths());
        if (this.conversionService != null) {
            result.initConversion(this.conversionService);
        }
        if (this.messageCodesResolver != null) {
            result.setMessageCodesResolver(this.messageCodesResolver);
        }
        return result;
    }

    ...
    // 把屬性訪問器返回,PropertyAccessor(默認(rèn)直接從結(jié)果里拿),子類MapDataBinder有復(fù)寫
    protected ConfigurablePropertyAccessor getPropertyAccessor() {
        return getInternalBindingResult().getPropertyAccessor();
    }

    // 可以看到簡單的轉(zhuǎn)換器也是使用到了conversionService的,可見conversionService它的效用
    protected SimpleTypeConverter getSimpleTypeConverter() {
        if (this.typeConverter == null) {
            this.typeConverter = new SimpleTypeConverter();
            if (this.conversionService != null) {
                this.typeConverter.setConversionService(this.conversionService);
            }
        }
        return this.typeConverter;
    }

    ... // 省略眾多get方法
    
    // 設(shè)置指定的可以綁定的字段,默認(rèn)是所有字段~~~
    // 例如,在綁定HTTP請(qǐng)求參數(shù)時(shí),限制這一點(diǎn)以避免惡意用戶進(jìn)行不必要的修改。
    // 簡單的說:我可以控制只有指定的一些屬性才允許你修改~~~~
    // 注意:它支持xxx*,*xxx,*xxx*這樣的通配符  支持[]這樣子來寫~
    public void setAllowedFields(@Nullable String... allowedFields) {
        this.allowedFields = PropertyAccessorUtils.canonicalPropertyNames(allowedFields);
    }
    public void setDisallowedFields(@Nullable String... disallowedFields) {
        this.disallowedFields = PropertyAccessorUtils.canonicalPropertyNames(disallowedFields);
    }

    // 注冊(cè)每個(gè)綁定進(jìn)程所必須的字段。
    public void setRequiredFields(@Nullable String... requiredFields) {
        this.requiredFields = PropertyAccessorUtils.canonicalPropertyNames(requiredFields);
        if (logger.isDebugEnabled()) {
            logger.debug("DataBinder requires binding of required fields [" + StringUtils.arrayToCommaDelimitedString(requiredFields) + "]");
        }
    }
    ...
    // 注意:這個(gè)是set方法,后面是有add方法的~
    // 注意:雖然是set,但是引用是木有變的~~~~
    public void setValidator(@Nullable Validator validator) {
        // 判斷邏輯在下面:你的validator至少得支持這種類型呀  哈哈
        assertValidators(validator);
        // 因?yàn)樽约菏謩?dòng)設(shè)置了,所以先清空  再加進(jìn)來~~~
        // 這步你會(huì)發(fā)現(xiàn),即使validator是null,也是會(huì)clear的哦~  符合語意
        this.validators.clear();
        if (validator != null) {
            this.validators.add(validator);
        }
    }
    private void assertValidators(Validator... validators) {
        Object target = getTarget();
        for (Validator validator : validators) {
            if (validator != null && (target != null && !validator.supports(target.getClass()))) {
                throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + target);
            }
        }
    }
    public void addValidators(Validator... validators) {
        assertValidators(validators);
        this.validators.addAll(Arrays.asList(validators));
    }
    // 效果同set
    public void replaceValidators(Validator... validators) {
        assertValidators(validators);
        this.validators.clear();
        this.validators.addAll(Arrays.asList(validators));
    }
    
    // 返回一個(gè),也就是primary默認(rèn)的校驗(yàn)器
    @Nullable
    public Validator getValidator() {
        return (!this.validators.isEmpty() ? this.validators.get(0) : null);
    }
    // 只讀視圖
    public List getValidators() {
        return Collections.unmodifiableList(this.validators);
    }

    // since Spring 3.0
    public void setConversionService(@Nullable ConversionService conversionService) {
        Assert.state(this.conversionService == null, "DataBinder is already initialized with ConversionService");
        this.conversionService = conversionService;
        if (this.bindingResult != null && conversionService != null) {
            this.bindingResult.initConversion(conversionService);
        }
    }

    // =============下面它提供了非常多的addCustomFormatter()方法  注冊(cè)進(jìn)PropertyEditorRegistry里=====================
    public void addCustomFormatter(Formatter formatter);
    public void addCustomFormatter(Formatter formatter, String... fields);
    public void addCustomFormatter(Formatter formatter, Class... fieldTypes);

    // 實(shí)現(xiàn)接口方法
    public void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor);
    public void registerCustomEditor(@Nullable Class requiredType, @Nullable String field, PropertyEditor propertyEditor);
    ...
    // 實(shí)現(xiàn)接口方法
    // 統(tǒng)一委托給持有的TypeConverter~~或者是getInternalBindingResult().getPropertyAccessor();這里面的
    @Override
    @Nullable
    public  T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType,
            @Nullable MethodParameter methodParam) throws TypeMismatchException {

        return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
    }


    // ===========上面的方法都是開胃小菜,下面才是本類最重要的方法==============

    // 該方法就是把提供的屬性值們,綁定到目標(biāo)對(duì)象target里去~~~
    public void bind(PropertyValues pvs) {
        MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues ? (MutablePropertyValues) pvs : new MutablePropertyValues(pvs));
        doBind(mpvs);
    }
    // 此方法是protected的,子類WebDataBinder有復(fù)寫~~~加強(qiáng)了一下
    protected void doBind(MutablePropertyValues mpvs) {
        // 前面兩個(gè)check就不解釋了,重點(diǎn)看看applyPropertyValues(mpvs)這個(gè)方法~
        checkAllowedFields(mpvs);
        checkRequiredFields(mpvs);
        applyPropertyValues(mpvs);
    }

    // allowe允許的 并且還是沒有在disallowed里面的 這個(gè)字段就是被允許的
    protected boolean isAllowed(String field) {
        String[] allowed = getAllowedFields();
        String[] disallowed = getDisallowedFields();
        return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
                (ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field)));
    }
    ...
    // protected 方法,給target賦值~~~~
    protected void applyPropertyValues(MutablePropertyValues mpvs) {
        try {
            // 可以看到最終賦值 是委托給PropertyAccessor去完成的
            getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());

        // 拋出異常,交給BindingErrorProcessor一個(gè)個(gè)處理~~~
        } catch (PropertyBatchUpdateException ex) {
            for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
                getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
            }
        }
    }

    // 執(zhí)行校驗(yàn),此處就和BindingResult 關(guān)聯(lián)上了,校驗(yàn)失敗的消息都會(huì)放進(jìn)去(不是直接拋出異常哦~ )
    public void validate() {
        Object target = getTarget();
        Assert.state(target != null, "No target to validate");
        BindingResult bindingResult = getBindingResult();
        // 每個(gè)Validator都會(huì)執(zhí)行~~~~
        for (Validator validator : getValidators()) {
            validator.validate(target, bindingResult);
        }
    }

    // 帶有校驗(yàn)提示的校驗(yàn)器。SmartValidator
    // @since 3.1
    public void validate(Object... validationHints) { ... }

    // 這一步也挺有意思:實(shí)際上就是若有錯(cuò)誤,就拋出異常
    // 若沒錯(cuò)誤  就把綁定的Model返回~~~(可以看到BindingResult里也能拿到最終值哦~~~)
    // 此方法可以調(diào)用,但一般較少使用~
    public Map close() throws BindException {
        if (getBindingResult().hasErrors()) {
            throw new BindException(getBindingResult());
        }
        return getBindingResult().getModel();
    }
}

從源源碼的分析中,大概能總結(jié)到DataBinder它提供了如下能力:

把屬性值PropertyValues綁定到target上(bind()方法,依賴于PropertyAccessor實(shí)現(xiàn)~)

提供校驗(yàn)的能力:提供了public方法validate()對(duì)各個(gè)屬性使用Validator執(zhí)行校驗(yàn)~

提供了注冊(cè)屬性編輯器(PropertyEditor)和對(duì)類型進(jìn)行轉(zhuǎn)換的能力(TypeConverter

總結(jié)

本文介紹了Spring用于數(shù)據(jù)綁定的最直接類DataBinder,它位于spring-context這個(gè)工程的org.springframework.validation包內(nèi),所以需要再次明確的是:它是Spring提供的能力而非web提供的~

雖然我們DataBinder是Spring提供,但其實(shí)把它發(fā)揚(yáng)光大是發(fā)生在Web環(huán)境,也就是大名鼎鼎的WebDataBinder,畢竟我們知道一般只有進(jìn)行web交互的時(shí)候,才會(huì)涉及到字符串 -> Java類型/對(duì)象的轉(zhuǎn)換,這就是下個(gè)章節(jié)講述的重中之重~

知識(shí)交流
若文章格式混亂,可點(diǎn)擊:原文鏈接-原文鏈接-原文鏈接-原文鏈接-原文鏈接

==The last:如果覺得本文對(duì)你有幫助,不妨點(diǎn)個(gè)贊唄。當(dāng)然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==

**若對(duì)技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群。
若群二維碼失效,請(qǐng)加wx號(hào):fsx641385712(或者掃描下方wx二維碼)。并且備注:"java入群" 字樣,會(huì)手動(dòng)邀請(qǐng)入群**

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75484.html

相關(guān)文章

  • @Validated和@Valid的區(qū)別?校驗(yàn)級(jí)聯(lián)屬性(內(nèi)部類)

    摘要:畢竟永遠(yuǎn)相信本文能給你帶來意想不到的收獲使用示例關(guān)于數(shù)據(jù)校驗(yàn)這一塊在中的使用案例,我相信但凡有點(diǎn)經(jīng)驗(yàn)的程序員應(yīng)該沒有不會(huì)使用的,并且還不乏熟練的選手。 每篇一句 NBA里有兩大笑話:一是科比沒天賦,二是詹姆斯沒技術(shù) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validati...

    Winer 評(píng)論0 收藏0
  • 最全面闡述WebDataBinder理解Spring數(shù)據(jù)綁定

    摘要:每篇一句不要總問低級(jí)的問題,這樣的人要么懶,不愿意上網(wǎng)搜索,要么笨,一點(diǎn)獨(dú)立思考的能力都沒有相關(guān)閱讀小家聊聊中的數(shù)據(jù)綁定本尊源碼分析小家聊聊中的數(shù)據(jù)綁定屬性訪問器和實(shí)現(xiàn)類的使用小家聊聊中的數(shù)據(jù)綁定以及內(nèi)省和對(duì)感興趣可掃碼加 每篇一句 不要總問低級(jí)的問題,這樣的人要么懶,不愿意上網(wǎng)搜索,要么笨,一點(diǎn)獨(dú)立思考的能力都沒有 相關(guān)閱讀 【小家Spring】聊聊Spring中的數(shù)據(jù)綁定 --- ...

    cgspine 評(píng)論0 收藏0
  • 小家SpringSpring IoC是如何使用BeanWrapper和Java內(nèi)省結(jié)合起來給Be

    摘要:從層層委托的依賴關(guān)系可以看出,的依賴注入給屬性賦值是層層委托的最終給了內(nèi)省機(jī)制,這是框架設(shè)計(jì)精妙處之一。當(dāng)然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的若對(duì)技術(shù)內(nèi)容感興趣可以加入群交流高工架構(gòu)師群。 每篇一句 具備了技術(shù)深度,遇到問題可以快速定位并從根本上解決。有了技術(shù)深度之后,學(xué)習(xí)其它技術(shù)可以更快,再深入其它技術(shù)也就不會(huì)害怕 相關(guān)閱讀 【小家Spring】聊聊Spring中的...

    waruqi 評(píng)論0 收藏0
  • 詳敘BeanWrapper和PropertyDescriptor

    摘要:關(guān)于它的數(shù)據(jù)轉(zhuǎn)換使用了如下兩種機(jī)制隸屬于規(guī)范。這種類中的方法主要用于訪問私有的字段,且方法名符合某種命名規(guī)則。如果在兩個(gè)模塊之間傳遞信息,可以將信息封裝進(jìn)中,這種對(duì)象稱為值對(duì)象,或。 每篇一句 千古以來要飯的沒有要早飯的,知道為什么嗎? 相關(guān)閱讀 【小家Spring】聊聊Spring中的數(shù)據(jù)轉(zhuǎn)換:Converter、ConversionService、TypeConverter、Pro...

    APICloud 評(píng)論0 收藏0
  • 讓Controller支持對(duì)平鋪參數(shù)執(zhí)行@Valid數(shù)據(jù)校驗(yàn)

    摘要:方案一借助對(duì)方法級(jí)別數(shù)據(jù)校驗(yàn)的能力首先必須明確一點(diǎn)此能力屬于框架的,而部分框架。 每篇一句 在金字塔塔尖的是實(shí)踐,學(xué)而不思則罔,思而不學(xué)則殆(現(xiàn)在很多編程框架都只是教你碎片化的實(shí)踐) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(JSR303、JSR349、JSR380)Hibernate-Validation 6.x使用案例【小家Spr...

    printempw 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<