バインディングSpringのデータのWebDataBinder理解の最も包括的な博覧会

各1

常に低レベルの質問をしてはいけない、そのような人々は、いずれか、インターネット、または独立していないと思いますする愚かな、少し能力を検索する怠惰たくありません

関連読書

【スモールトーク】春に結合ホームスプリングデータ---のDataBinder神(ソースコード分析)

春--- PropertyAccessorをプロパティアクセサと実装クラスDirectFieldAccessorに話を結合[小さな家]春のデータを使用します

[春]春--- BeanWrapperとJavaイントロスペクションとのPropertyDescriptor Introspectorの中の小さな家族の話をデータバインディング


<センター>興味スプリングスキャン可能なコードは、WXグループを追加しました:Java高工、架构师3群(エンド二次元コード)</センター>


序文

前回の記事で話したDataBinder記事はについて話を続け、実用化データバインディングメインコースをWebDataBinder

上記に基づき、我々は見て取りDataBinder、その継承ツリーを:
ここに画像を挿入説明
あなたは、継承ツリーから見ることができ、データバインディング統一ウェブ環境がDataBinder強化されました。

結局のところ、実際のデータがシナリオを結合しない:例99%がウェブ環境であると言っても過言を -

WebDataBinder

その役割はからですweb request(内側:〜ヨーヨー要求は必ずしもたServletRequestではない、これはWeb要求を指します)のウェブがリクエストparametersにバインドされていますJavaBean-

Controllerパラメータタイプの方法は、基本的なタイプであってもよく、通常のJava型をカプセル化することができます。このタイプは、任意の一般的なJavaアノテーションを宣言しない場合は、それがあることを意味し每一个属性、対応するリクエストパラメータを見つけるために行くことを要求する必要があります。

// @since 1.2
public class WebDataBinder extends DataBinder {

    // 此字段意思是:字段标记  比如name -> _name
    // 这对于HTML复选框和选择选项特别有用。
    public static final String DEFAULT_FIELD_MARKER_PREFIX = "_";
    // !符号是处理默认值的,提供一个默认值代替空值~~~
    public static final String DEFAULT_FIELD_DEFAULT_PREFIX = "!";

    @Nullable
    private String fieldMarkerPrefix = DEFAULT_FIELD_MARKER_PREFIX;
    @Nullable
    private String fieldDefaultPrefix = DEFAULT_FIELD_DEFAULT_PREFIX;
    // 默认也会绑定空的文件流~
    private boolean bindEmptyMultipartFiles = true;

    // 完全沿用父类的两个构造~~~
    public WebDataBinder(@Nullable Object target) {
        super(target);
    }
    public WebDataBinder(@Nullable Object target, String objectName) {
        super(target, objectName);
    }

    ... //  省略get/set
    // 在父类的基础上,增加了对_和!的处理~~~
    @Override
    protected void doBind(MutablePropertyValues mpvs) {
        checkFieldDefaults(mpvs);
        checkFieldMarkers(mpvs);
        super.doBind(mpvs);
    }

    protected void checkFieldDefaults(MutablePropertyValues mpvs) {
        String fieldDefaultPrefix = getFieldDefaultPrefix();
        if (fieldDefaultPrefix != null) {
            PropertyValue[] pvArray = mpvs.getPropertyValues();
            for (PropertyValue pv : pvArray) {

                // 若你给定的PropertyValue的属性名确实是以!打头的  那就做处理如下:
                // 如果JavaBean的该属性可写 && mpvs不存在去掉!后的同名属性,那就添加进来表示后续可以使用了(毕竟是默认值,没有精确匹配的高的)
                // 然后把带!的给移除掉(因为默认值以已经转正了~~~)
                // 其实这里就是说你可以使用!来给个默认值。比如!name表示若找不到name这个属性的时,就取它的值~~~
                // 也就是说你request里若有穿!name保底,也就不怕出现null值啦~
                if (pv.getName().startsWith(fieldDefaultPrefix)) {
                    String field = pv.getName().substring(fieldDefaultPrefix.length());
                    if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
                        mpvs.add(field, pv.getValue());
                    }
                    mpvs.removePropertyValue(pv);
                }
            }
        }
    }

    // 处理_的步骤
    // 若传入的字段以_打头
    // JavaBean的这个属性可写 && mpvs木有去掉_后的属性名字
    // getEmptyValue(field, fieldType)就是根据Type类型给定默认值。
    // 比如Boolean类型默认给false,数组给空数组[],集合给空集合,Map给空map  可以参考此类:CollectionFactory
    // 当然,这一切都是建立在你传的属性值是以_打头的基础上的,Spring才会默认帮你处理这些默认值
    protected void checkFieldMarkers(MutablePropertyValues mpvs) {
        String fieldMarkerPrefix = getFieldMarkerPrefix();
        if (fieldMarkerPrefix != null) {
            PropertyValue[] pvArray = mpvs.getPropertyValues();
            for (PropertyValue pv : pvArray) {
                if (pv.getName().startsWith(fieldMarkerPrefix)) {
                    String field = pv.getName().substring(fieldMarkerPrefix.length());
                    if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
                        Class<?> fieldType = getPropertyAccessor().getPropertyType(field);
                        mpvs.add(field, getEmptyValue(field, fieldType));
                    }
                    mpvs.removePropertyValue(pv);
                }
            }
        }
    }

    // @since 5.0
    @Nullable
    public Object getEmptyValue(Class<?> fieldType) {
        try {
            if (boolean.class == fieldType || Boolean.class == fieldType) {
                // Special handling of boolean property.
                return Boolean.FALSE;
            } else if (fieldType.isArray()) {
                // Special handling of array property.
                return Array.newInstance(fieldType.getComponentType(), 0);
            } else if (Collection.class.isAssignableFrom(fieldType)) {
                return CollectionFactory.createCollection(fieldType, 0);
            } else if (Map.class.isAssignableFrom(fieldType)) {
                return CollectionFactory.createMap(fieldType, 0);
            }
        } catch (IllegalArgumentException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to create default value - falling back to null: " + ex.getMessage());
            }
        }
        // 若不在这几大类型内,就返回默认值null呗~~~
        // 但需要说明的是,若你是简单类型比如int,
        // Default value: null. 
        return null;
    }

    // 单独提供的方法,用于绑定org.springframework.web.multipart.MultipartFile类型的数据到JavaBean属性上~
    // 显然默认是允许MultipartFile作为Bean一个属性  参与绑定的
    // Map<String, List<MultipartFile>>它的key,一般来说就是文件们啦~
    protected void bindMultipart(Map<String, List<MultipartFile>> multipartFiles, MutablePropertyValues mpvs) {
        multipartFiles.forEach((key, values) -> {
            if (values.size() == 1) {
                MultipartFile value = values.get(0);
                if (isBindEmptyMultipartFiles() || !value.isEmpty()) {
                    mpvs.add(key, value);
                }
            }
            else {
                mpvs.add(key, values);
            }
        });
    }
}

以下からのシングルWebDataBinderも、その親クラスが提供するように拡張された拡張機能を次のように:

  1. プロパティ名のサポート_処理のデフォルト値で始まる(自動、することができます自動的にハンドルブール、コレクション、地図などのすべてを)
  2. 属性名のサポート!扱うデフォルト値で始まる(マニュアルトランスミッションは、手動でプロパティにデフォルト値を割り当てる必要があり、自分自身の非常に柔軟な制御)
  3. サポートするための方法の提供MultipartFileにバインドするJavaBeanプロパティにします-

Demoサンプル

その強化され、これらの機能の使用を実証する例下:

@Getter
@Setter
@ToString
public class Person {

    public String name;
    public Integer age;

    // 基本数据类型
    public Boolean flag;
    public int index;
    public List<String> list;
    public Map<String, String> map;

}

使用を証明!デフォルト値は手動で正確に制御フィールドを:

    public static void main(String[] args) {
        Person person = new Person();
        WebDataBinder binder = new WebDataBinder(person, "person");

        // 设置属性(此处演示一下默认值)
        MutablePropertyValues pvs = new MutablePropertyValues();

        // 使用!来模拟各个字段手动指定默认值
        //pvs.add("name", "fsx");
        pvs.add("!name", "不知火舞");
        pvs.add("age", 18);
        pvs.add("!age", 10); // 上面有确切的值了,默认值不会再生效

        binder.bind(pvs);
        System.out.println(person);
    }

プリントアウト(予想通り):

Person(name=null, age=null, flag=false, index=0, list=[], map={})

この印刷結果は、上記の結果を比較し、あなたは発見することがたくさんあるでしょうが、例えば、見つけることができます使用してくださいデフォルトの基本的なタイプは、独自のです
もう一つの理由は明確である:場合はnull間違いなくありますその結果、特別な取り扱い、包装タイプのデフォルト値〜

理解しWebDataBinderた後、我々は重要なサブクラスとして、それを見続けますServletRequestDataBinder

ServletRequestDataBinder

以前、何のプロはまた、我々はに話していた木を見つけることがないことを言った最も一般的なウェブシーンAPI: javax.servlet.ServletRequestこのクラスは、それがこれを行うために生まれた、名前から知っているだろう。

:その目標はにあるサーブレットからのデータのJavaBean、サポートマルチパートへのバインド・パラメータでサーブレット要求からマルチパートファイルのサポートを含むパラメータのJavaBeansへのバインディング要求..

注意:までそのような要求は、Webサーブレット要求のために定義された、と強くサーブレット仕様をバインドされています。


public class ServletRequestDataBinder extends WebDataBinder {
... // 沿用父类构造
// 注意这个可不是父类的方法,是本类增强的~~~~意思就是kv都从request里来~~当然内部还是适配成了一个MutablePropertyValues
public void bind(ServletRequest request) {
// 内部最核心方法是它:WebUtils.getParametersStartingWith()  把request参数转换成一个Map
// request.getParameterNames()
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
    // 调用父类的bindMultipart方法,把MultipartFile都放进MutablePropertyValues里去~~~
    if (multipartRequest != null) {
        bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
    }
    // 这个方法是本类流出来的一个扩展点~~~子类可以复写此方法自己往里继续添加
    // 比如ExtendedServletRequestDataBinder它就复写了这个方法,进行了增强(下面会说)  支持到了uriTemplateVariables的绑定
    addBindValues(mpvs, request);
    doBind(mpvs);
}

// 这个方法和父类的close方法类似,很少直接调用
public void closeNoCatch() throws ServletRequestBindingException {
    if (getBindingResult().hasErrors()) {
        throw new ServletRequestBindingException("Errors binding onto object '" + getBindingResult().getObjectName() + "'", new BindException(getBindingResult()));
    }
}

}

下面就以`MockHttpServletRequest`为例作为Web 请求实体,演示一个使用的小Demo。说明:`MockHttpServletRequest`它是`HttpServletRequest`的实现类~
#### Demo示例
```java
    public static void main(String[] args) {
        Person person = new Person();
        ServletRequestDataBinder binder = new ServletRequestDataBinder(person, "person");

        // 构造参数,此处就不用MutablePropertyValues,以HttpServletRequest的实现类MockHttpServletRequest为例吧
        MockHttpServletRequest request = new MockHttpServletRequest();
        // 模拟请求参数
        request.addParameter("name", "fsx");
        request.addParameter("age", "18");

        // flag不仅仅可以用true/false  用0和1也是可以的?
        request.addParameter("flag", "1");

        // 设置多值的
        request.addParameter("list", "4", "2", "3", "1");
        // 给map赋值(Json串)
        // request.addParameter("map", "{'key1':'value1','key2':'value2'}"); // 这样可不行
        request.addParameter("map['key1']", "value1");
        request.addParameter("map['key2']", "value2");

        //// 一次性设置多个值(传入Map)
        //request.setParameters(new HashMap<String, Object>() {{
        //    put("name", "fsx");
        //    put("age", "18");
        //}});

        binder.bind(request);
        System.out.println(person);
    }

プリントアウト:

Person(name=fsx, age=18, flag=true, index=0, list=[4, 2, 3, 1], map={key1=value1, key2=value2})

パーフェクト。

質問:なぜジュニアパートナーは、値のJSONがライン上でそれを書き込みませクロッシング値が上記されているマップと思うことがありますか?

ExtendedServletRequestDataBinder

このようなコードは小さいが、過小評価することはできません、それがあるServletRequestDataBinderために使用され、強化されたURI template variables結合のために来てパラメータを追加します。これは、リクエストから行くHandlerMapping.class.getName() + ".uriTemplateVariables";の結合に使用するために、このプロパティの値を見つけるために~~~

例えば、我々は精通している@PathVariableこと、それが関連:それはアウトurlパラメータのテンプレートを解析するための責任があり、その後、最終的にするためのattrに置く、とExtendedServletRequestDataBinder結合し~~~

この間を:私は〜それが結合するために、当社のグローバル変数をカスタマイズすることである役割があると思います

このプロパティの値は置く場所は次のとおりです。AbstractUrlHandlerMapping.lookupHandler()- > chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));- > preHandle()方法- > exposeUriTemplateVariables(this.uriTemplateVariables, request);- >request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);


// @since 3.1
public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder {
... // 沿用父类构造
//本类的唯一方法
@Override
@SuppressWarnings("unchecked")
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
    // 它的值是:HandlerMapping.class.getName() + ".uriTemplateVariables";
    String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;

    // 注意:此处是attr,而不是parameter
    Map<String, String> uriVars = (Map<String, String>) request.getAttribute(attr);
    if (uriVars != null) {
        uriVars.forEach((name, value) -> {

            // 若已经存在确切的key了,不会覆盖~~~~
            if (mpvs.contains(name)) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Skipping URI variable '" + name + "' because request contains bind value with same name.");
                }
            } else {
                mpvs.addPropertyValue(name, value);
            }
        });
    }
}

}

可见,通过它我们亦可以很方便的做到在每个`ServletRequest`提供一份共用的模版属性们,供以绑定~

此类基本都沿用父类的功能,比较简单,此处就不写Demo了(Demo请参照父类)~

> 说明:`ServletRequestDataBinder`一般不会直接使用,而是使用更强的子类`ExtendedServletRequestDataBinder`

### WebExchangeDataBinder
它是`Spring5.0`后提供的,对`Reactive`编程的Mono数据绑定提供支持,因此暂略~
```java
data binding from URL query params or form data in the request data to Java objects

MapDataBinder

これは、に位置しているorg.springframework.data.web春のデータの相関関係と、特に治療のためtargetであるMap&lt;String, Object&gt;ターゲット・オブジェクト型をバインドするために、それは公共のクラスではありません〜

これは、プロパティアクセサに使用されているMapPropertyAccessor:から継承されたAbstractPropertyAccessorプライベート静的内部クラス〜(もああSPELをサポートするために)

WebRequestDataBinder

春の独自の定義の治療に使用されorg.springframework.web.context.request.WebRequest、その後細部への機会今回、詳細〜を持って、処理およびコンテナに依存しないWebリクエストデータバインディングの目的を


どのように実装するために、独自のPropertyEditor登録するには自定义类型、データバインディングを?

我々は以前の分析によって知っている、そしてこれをデータバインディングは、最終的に依存していますPropertyEditor(すべての要求は、文字列まあ〜に渡された後)は、特定の属性変換値を達成するために

一般的には、文字列のように、int型は、長いパラメータが自動的にバインドされ、デフォルトでは、春は私たちにパーサNの番号を登録されている、ということがあり、フロントので、自動的に行うことができますバインディング。

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {

    @Nullable
    private Map<Class<?>, PropertyEditor> defaultEditors;

    private void createDefaultEditors() {
        this.defaultEditors = new HashMap<>(64);

        // Simple editors, without parameterization capabilities.
        // The JDK does not contain a default editor for any of these target types.
        this.defaultEditors.put(Charset.class, new CharsetEditor());
        this.defaultEditors.put(Class.class, new ClassEditor());
        ...
        // Default instances of collection editors.
        // Can be overridden by registering custom instances of those as custom editors.
        this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
        this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
        this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
        this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
        this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
        ...
        // 这里就部全部枚举出来了
    }
}

多くは、デフォルトの登録エディタをサポートしていますが、まだそれ見つけながら、様々なイベントや無Date型、およびJsr310は(もちろん、私たちのカスタム型を含む)の変換の日付を入力し、提供しました。
だから私は、小さなパートナーは、このような痛みのポイントを経験していると信じている:日付は、LOCALDATE使用の他の種類を自動的に不便古い、そして多くの場合、無邪気に混乱してバインドされています。だから、最終的にそれらの多くはしぶしぶセマンティクスを選んだのは非常に明確ではありませんタイムスタンプ合格します

日付は、デモデータバインディングのタイプを示します。

@Getter
@Setter
@ToString
public class Person {

    public String name;
    public Integer age;

    // 以Date类型为示例
    private Date start;
    private Date end;
    private Date endTest;

}

    public static void main(String[] args) {
        Person person = new Person();
        DataBinder binder = new DataBinder(person, "person");

        // 设置属性
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.add("name", "fsx");

        // 事件类型绑定
        pvs.add("start", new Date());
        pvs.add("end", "2019-07-20");
        // 试用试用标准的事件日期字符串形式~
        pvs.add("endTest", "Sat Jul 20 11:00:22 CST 2019");

        binder.bind(pvs);
        System.out.println(person);
    }

プリントアウト:

Person(name=fsx, age=null, start=Sat Jul 20 11:05:29 CST 2019, end=null, endTest=Sun Jul 21 01:00:22 CST 2019)

結果は私の期待に沿ったものである:スタートは値を持っている、終わり、ENDTEST値が存在します。
スモールスタートの可能なパートナーが、最後は理解することができ、最も驚くべきは、あるendTest値がありますなぜですか?
ここで私は、簡単にプロセスステップを説明します。

  1. BeanWrapperコールはsetPropertyValue()値で渡された値が与えられますそれらを割り当てますconvertForProperty()(たとえば、ここでは日付型など)に到達するために従ったメソッドの戻り値の型変換方法
  2. 委託this.typeConverterDelegate.convertIfNecessary変換(ここでは、例えばはstring->日付タイプ)を入力
  3. まずthis.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);、適切なを見つけるPropertyEditor(どうやらここで私たちはヌルカスタムのPropertyEditorカスタム日付を返すに対処していません)
  4. フォールバックConversionService明らかに、我々はここで設定されていないしている、またはnull
  5. デフォルトを使用するにフォールバックeditor = findDefaultEditor(requiredType);(注意:ここでの唯一の種類に応じて検索し、それがデフォルトの日で対処されていないと言うので、それはまた、nullを返します)
  6. 最後の最後の、春は背面に落ちArray、Collection、Map、問題に対処のデフォルト値文字列型が呼び出されます最終的に場合BeanUtils.instantiateClass(strCtor, convertedValue)があり、ある引数のコンストラクタは(これが唯一のString型の右でなければならないことに注意してください)~~~を初期化
    1. したがって、本実施形態では、最後のステップに相当するnew Date("Sat Jul 20 11:00:22 CST 2019")〜すなわち通常ENDTESTを割り当てることができ、文字列が標準であるため、日付文字列が非常に広い器具であります

この簡単なステップ分析を通して理由を説明終了値なし、ENDTESTは価値があるのを。
実際には、のフォールバックによる処理の最後のステップは、我々はまた、この行うことができます独創的なアプリケーションを例えば、私は、次の例の巧妙な使用を与えます:

@Getter
@Setter
@ToString
public class Person {
    private String name;
    // 备注:child是有有一个入参的构造器的
    private Child child;
}

@Getter
@Setter
@ToString
public class Child {
    private String name;
    private Integer age;
    public Child() {
    }
    public Child(String name) {
        this.name = name;
    }
}

    public static void main(String[] args) {
        Person person = new Person();
        DataBinder binder = new DataBinder(person, "person");

        // 设置属性
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.add("name", "fsx");

        // 给child赋值,其实也可以传一个字符串就行了 非常的方便   Spring会自动给我们new对象
        pvs.add("child", "fsx-son");

        binder.bind(pvs);
        System.out.println(person);
    }

プリントアウト:

Person(name=fsx, child=Child(name=fsx-son, age=null))

パーフェクト。


ADOは、ここで私通じカスタムプロパティエディタたちが着信上記のプロセスをサポートすることを可能にすることを意味し2019-07-20、この時刻文字列の非標準を

我々はことを知ってDataBinder、それ自体があるPropertyEditorRegistryので、私はちょうどカスタム登録を所有する必要がありますPropertyEditorに:

1、継承によってPropertyEditorSupport、独自の処理日付エディタを達成するために:

public class MyDatePropertyEditor extends PropertyEditorSupport {

    private static final String PATTERN = "yyyy-MM-dd";

    @Override
    public String getAsText() {
        Date date = (Date) super.getValue();
        return new SimpleDateFormat(PATTERN).format(date);
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        try {
            super.setValue(new SimpleDateFormat(PATTERN).parse(text));
        } catch (ParseException e) {
            System.out.println("ParseException....................");
        }
    }
}

2、登録へDataBinderと実行

    public static void main(String[] args) {
        Person person = new Person();
        DataBinder binder = new DataBinder(person, "person");
        binder.registerCustomEditor(Date.class, new MyDatePropertyEditor());
        //binder.registerCustomEditor(Date.class, "end", new MyDatePropertyEditor());

        // 设置属性
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.add("name", "fsx");

        // 事件类型绑定
        pvs.add("start", new Date());
        pvs.add("end", "2019-07-20");
        // 试用试用标准的事件日期字符串形式~
        pvs.add("endTest", "Sat Jul 20 11:00:22 CST 2019");

        binder.bind(pvs);
        System.out.println(person);
    }

次のように実行して印刷します:

ParseException....................
Person(name=fsx, age=null, start=Sat Jul 20 11:41:49 CST 2019, end=Sat Jul 20 00:00:00 CST 2019, endTest=null)

期待に沿った結果しかし、私はまだ自分自身が考えて、小さなパートナーのために、次の二つの質問を投げるこの結果:
1、出力....................はParseException
2は、スタート値、ENDTESTを持っていますしかし、値はnullです

最後に、私はこれを理解したいと思います:カスタムエディタを、我々はパッケージの非常に自由な、高度にカスタマイズされた完全なカスタムタイプすることができ、我々はさらに多くの障害が、寛容、よりインテリジェントな、よりシンプルコントローラを作ることができます。利害関係者は、練習の自己の知識のこの作品を使用することができます〜

WebBindingInitializer和WebDataBinderFactory

WebBindingInitializer

WebBindingInitializer:このインタフェースは、プロパティエディタを登録する方法initBinder書き換えが実装されているグローバルコントローラのすべての有効なプロパティエディタ、。

これは、単純な、粗として理解することができる:WebBindingInitializer符号化として、@InitBinder方法をコメントする(もちろん、よりきめの細かい制御を実現する唯一の効果的な電流コントローラを制御するための方法を注記します)

このインタフェースの観測、春の命名は非常に興味深いです:それは、バインディングを使用して進行状態に

// @since 2.5   Spring在初始化WebDataBinder时候的回调接口,给调用者自定义~
public interface WebBindingInitializer {

    // @since 5.0
    void initBinder(WebDataBinder binder);

    // @deprecated as of 5.0 in favor of {@link #initBinder(WebDataBinder)}
    @Deprecated
    default void initBinder(WebDataBinder binder, WebRequest request) {
        initBinder(binder);
    }

}

このインタフェースは、内蔵の実装クラスがユニークです:ConfigurableWebBindingInitializer拡張したい場合は、それを継承することをお勧めします〜

public class ConfigurableWebBindingInitializer implements WebBindingInitializer {
    private boolean autoGrowNestedPaths = true;
    private boolean directFieldAccess = false; // 显然这里是false

    // 下面这些参数,不就是WebDataBinder那些可以配置的属性们吗?
    @Nullable
    private MessageCodesResolver messageCodesResolver;
    @Nullable
    private BindingErrorProcessor bindingErrorProcessor;
    @Nullable
    private Validator validator;
    @Nullable
    private ConversionService conversionService;
    // 此处使用的PropertyEditorRegistrar来管理的,最终都会被注册进PropertyEditorRegistry嘛
    @Nullable
    private PropertyEditorRegistrar[] propertyEditorRegistrars;

    ... //  省略所有get/set

    // 它做的事无非就是把配置的值都放进去而已~~
    @Override
    public void initBinder(WebDataBinder binder) {
        binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
        if (this.directFieldAccess) {
            binder.initDirectFieldAccess();
        }
        if (this.messageCodesResolver != null) {
            binder.setMessageCodesResolver(this.messageCodesResolver);
        }
        if (this.bindingErrorProcessor != null) {
            binder.setBindingErrorProcessor(this.bindingErrorProcessor);
        }
        // 可以看到对校验器这块  内部还是做了容错的
        if (this.validator != null && binder.getTarget() != null && this.validator.supports(binder.getTarget().getClass())) {
            binder.setValidator(this.validator);
        }
        if (this.conversionService != null) {
            binder.setConversionService(this.conversionService);
        }
        if (this.propertyEditorRegistrars != null) {
            for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
                propertyEditorRegistrar.registerCustomEditors(binder);
            }
        }
    }
}

この実装クラスは、主に使用して簡単にいくつかの設定可能な項目が用意されています。注:このインタフェースは、一般的に直接使用されるが、組み合わせにされていないInitBinderDataBinderFactoryWebDataBinderFactoryなど-で使用するために

WebDataBinderFactory

名前が示すように、それは作成することですWebDataBinder工場を。

// @since 3.1   注意:WebDataBinder 可是1.2就有了~
public interface WebDataBinderFactory {
    // 此处使用的是Spring自己的NativeWebRequest   后面两个参数就不解释了
    WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception;
}

これは、継承ツリー以下のように:
ここに画像を挿入説明

DefaultDataBinderFactory

public class DefaultDataBinderFactory implements WebDataBinderFactory {
    @Nullable
    private final WebBindingInitializer initializer;
    // 注意:这是唯一构造函数
    public DefaultDataBinderFactory(@Nullable WebBindingInitializer initializer) {
        this.initializer = initializer;
    }

    // 实现接口的方法
    @Override
    @SuppressWarnings("deprecation")
    public final WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {

        WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);

        // 可见WebDataBinder 创建好后,此处就会回调(只有一个)
        if (this.initializer != null) {
            this.initializer.initBinder(dataBinder, webRequest);
        }
        // 空方法 子类去实现,比如InitBinderDataBinderFactory实现了词方法
        initBinder(dataBinder, webRequest);
        return dataBinder;
    }

    //  子类可以复写,默认实现是WebRequestDataBinder
    // 比如子类ServletRequestDataBinderFactory就复写了,使用的new ExtendedServletRequestDataBinder(target, objectName)
    protected WebDataBinder createBinderInstance(@Nullable Object target, String objectName, NativeWebRequest webRequest) throws Exception 
        return new WebRequestDataBinder(target, objectName);
    }
}

スプリング一貫した設計によれば、本発明の方法は、サブクラス複製効果が操作に応じ達成することができ、テンプレートの動作を可能にします。

InitBinderDataBinderFactory

これは、から継承DefaultDataBinderFactory主で標識した処理のために、@InitBinder〜最初の結合を実行する方法

// @since 3.1
public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {

    // 需要注意的是:`@InitBinder`可以标注N多个方法~  所以此处是List
    private final List<InvocableHandlerMethod> binderMethods;

    // 此子类的唯一构造函数
    public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods, @Nullable WebBindingInitializer initializer) {
        super(initializer);
        this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());
    }

    // 上面知道此方法的调用方法生initializer.initBinder之后
    // 所以使用注解它生效的时机是在直接实现接口的后面的~
    @Override
    public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
        for (InvocableHandlerMethod binderMethod : this.binderMethods) {
            // 判断@InitBinder是否对dataBinder持有的target对象生效~~~(根据name来匹配的)
            if (isBinderMethodApplicable(binderMethod, dataBinder)) {
                // 关于目标方法执行这块,可以参考另外一篇@InitBinder的原理说明~
                Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);

                // 标注@InitBinder的方法不能有返回值
                if (returnValue != null) {
                    throw new IllegalStateException("@InitBinder methods must not return a value (should be void): " + binderMethod);
                }
            }
        }
    }

    //@InitBinder有个Value值,它是个数组。它是用来匹配dataBinder.getObjectName()是否匹配的   若匹配上了,现在此注解方法就会生效
    // 若value为空,那就对所有生效~~~
    protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder dataBinder) {
        InitBinder ann = initBinderMethod.getMethodAnnotation(InitBinder.class);
        Assert.state(ann != null, "No InitBinder annotation");
        String[] names = ann.value();
        return (ObjectUtils.isEmpty(names) || ObjectUtils.containsElement(names, dataBinder.getObjectName()));
    }
}
ServletRequestDataBinderFactory

これは、から継承InitBinderDataBinderFactoryも、より明白な役割。どちらも、扱うことができ@InitBinder、そしてそれは、より強力なデータバインドコントロールを使用しています。ExtendedServletRequestDataBinder

// @since 3.1
public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory {
    public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods, @Nullable WebBindingInitializer initializer) {
        super(binderMethods, initializer);
    }
    @Override
    protected ServletRequestDataBinder createBinderInstance(
            @Nullable Object target, String objectName, NativeWebRequest request) throws Exception  {
        return new ExtendedServletRequestDataBinder(target, objectName);
    }
}

この植物は、RequestMappingHandlerAdapterデフォルトのアダプタは、データバインディングの工場を使用し、RequestMappingHandlerAdapterまだ現時点では最も頻繁に使用される、最も強力なもののアダプタです

概要

WebDataBinderSpringMVC使用、それは私たち自身を作成する必要はありません、我々は唯一のプロパティエディタの種類に対応するパラメータに登録する必要がありますPropertyEditorPropertyEditor文字列は、に変換することができる真のデータ・タイプ、そのvoid setAsText(String text)データ変換処理の実現方法。

十分に把握してこの部分、Spring MVCと組み合わせて@InitBinder一緒に使用するノート、非常に大きな力を持つことになり、ある程度の開発を簡素化し、効率を改善

知識交換

若文章格式混乱,可点击説明リンク-テキストリンク-テキストリンク-テキストリンク-テキストリンク

==最後に:あなたはあなたにこの記事が参考に思われる場合は、賞賛の聖歌を指すように望むことができます。もちろん、友人のサークルは、より小さなパートナーも見ているので、共有する作者本人许可的~==を

技術的な内容に興味があれば×××ストリームWX追加することができますJava高工、架构师3群
グループは、2次元コードを失敗した場合、WX番号を追加してください:fsx641385712(または2次元コードがWXの下でスキャンされます)。そして注意:"java入群"単語は、手動でグループに招待されます

![ここに画像の説明を挿入](Https://img-blog.csdnimg.cn/20190703175020107.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Y2NDEzODU3MTI =、size_16、color_FFFFFF、t_70、pic_center = 300X)

おすすめ

転載: blog.51cto.com/3631118/2422034