違い@Validatedと@Valid?(内部)カスケードプロパティをチェックします

各1

2人のNBAのジョークがあります。一つは才能のブライアント、ジェームズ、そして第二に、ない技術ではありません

関連読書

[Javaの]データ検証の小さな家庭深い理解:Java Beanが検証2.0(JSR303、 JSR349、JSR380)6.xの-検証休止状態のユースケースを
スプリングハウスコントローラは、データの検証がタイルパラメータの実装をサポートすることができます(Spring MVCのデフォルト[小] @ValidだけのJavaBeanに確認することができます使用)
[小]春のホーム春のメソッドレベルのデータの検証:@Validated + MethodValidationPostProcessor上品な仕上がりのデータ検証動作


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


序文

記事では説明Springメソッドレベルのデータ検証のエレガントな環境の実現を、そしてヒントを植え:それはSpring MVCControllerどのようにアプリケーション層)、その中に?この記事では、この目的のために立ち上げた説明するために、引き続きSpring MVCデータ検証中-

小さなパートナーはすぐに思ったことができるかもしれません:これは同じではありませんか?私たちは、使用しControllerているメソッドレベルを、それが唯一の方法・レベルの検証を直接適用である-この質問のために私は答えるが、その後、あなたが理解したいことができるはずホメオパシー二つの質問をスローしません。

  1. 私は、チェックのレベルに基づいて、上記の方法を述べているSpringデフォルトと未开启、なぜあなたがしているSpring MVCことを直接使用することができ@Valid、それをチェック完了するには?
    1. いくつかの小さなパートナーは、彼が言うかもしれないSpringBoot、それはありませんが、デフォルトをオープンする可能性が高いです。あなたは伝統的な使用する場合でもSpring MVC、あなたは見つけるでしょうあなたがしようとは思わない、すぐに利用可能です
  2. ここに類推:あるアイデアの実現は、しかし、あなたが起動しない場合でも、あなたは発見していないサポートが、それはまだHaoshi〜ですSpring MVCHandlerInterceptorAOP@EnableAspectJAutoProxy

あなたはその下に、私が提起これらの2つの問題を理解したい場合は理解することは非常に困難です。もちろん、あなたがこれらの二つの質問に対する答えを知っている場合でも、あなたが読むことをお勧めします。すべての後:私は〜、これはあなたの予想外の収穫を与えるだろうと考えていることはありません

使用例

これは、データのチェックでSpring MVC、私はいつでもJavaプログラマの少し経験が使用されないべきではないと信じているユースケースと、またの不足熟練したプレイヤーその前に私は単に“采访”プログラマの大半は、春にデータ検証が参照一度も考えていたControllerの使用@Validated上院にあるチェックJavaBean〜この1

次の例だから、あなたには見知らぬ人はいけません。

@Getter
@Setter
@ToString
public class Person {

    @NotNull
    private String name;
    @NotNull
    @Positive
    private Integer age;

    @Valid // 让InnerChild的属性也参与校验
    @NotNull
    private InnerChild child;

    @Getter
    @Setter
    @ToString
    public static class InnerChild {
        @NotNull
        private String name;
        @NotNull
        @Positive
        private Integer age;
    }

}

@RestController
@RequestMapping
public class HelloController {

    @PostMapping("/hello")
    public Object helloPost(@Valid @RequestBody Person person, BindingResult result) {
        System.out.println(result.getErrorCount());
        System.out.println(result.getAllErrors());
        return person;
    }
}

POSTリクエストを送信する:/hello Content-Type=application/json次のように、JSON文字列が渡されました。

{
  "name" : "fsx",
  "age" : "-1",
  "child" : {
    "age" : 1
  }
}

印刷コンソール次のとおりです。

2
[Field error in object 'person' on field 'child.name': rejected value [null]; codes [NotNull.person.child.name,NotNull.child.name,NotNull.name,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.child.name,child.name]; arguments []; default message [child.name]]; default message [不能为null], Field error in object 'person' on field 'age': rejected value [-1]; codes [Positive.person.age,Positive.age,Positive.java.lang.Integer,Positive]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.age,age]; arguments []; default message [age]]; default message [必须是正数]]

ビューの印刷の観点から:発効をチェック(エラーメッセージを保持することは前の表示に戻り、または行いますエラーページに移動できます)。

この例では、2つの小さな細部に注意することが重要です。

  1. @RequestBody注意事項は、(結合入ってくるJSONデータを完了することができないそうでない場合は省略することができない拘束力はない、検証がああ有効になっている場合でも、〜)
  2. 以下のための参照方法に書き込まれていない場合はBindingResult result、このパラメータのチェックが失敗した場合、サーバーが例外をスローしますので、リクエストは直接、400エラーを得ていますorg.springframework.web.bind.MethodArgumentNotValidException書かれている場合、それは自分自身の呼び出し元に対処するだろう自分〜

中に小さなパートナーの実際の使用をカバーするために、この場合の私の未熟と不完全な統計によると90%、実際の使用シナリオよりも非常に、シンプルでエレガントな、効率的な、本当に使い-

しかし、経験豊富なプログラマーとして、あなた、あなたが使用しても@Valid、データ検証の上品な仕上がりを、しかしバックはあなたのコードがたくさん残っているでしょうかどうかであるif else基本的なチェックのは?その理由は何ですか?実際には、唯一の根本的な理由がある:多くの場合@Validがカバーしていない使用して、それが唯一のJavaBeanを確認することができますので
、私はあなたがこの種の持っていると信じて痛みのポイントを使用することを原則レベル本論文で始まり、その後、あなたが遭遇した痛みのポイントを与えますリファレンスシナリオを解決参照の問題-

原理分析

Controller提供使って@Valid便利なチェックJavaBeanの原則を、そして春のレベルのキャリブレーション方法(類推は大きな違いがあることが原則サポートすることができSpring MVCインターセプタとSpring AOP〜の違いの違いを)、そして今、この権利を見てみましょう

無視しないでください优雅電源のコードを、それが指数関数的にメンテナンスコストを削減し、あなたの符号化効率を向上させる倍増、あるいはあなたがバグを書き指数関数的に拡張性と二重の削減の可能性を高めます-

思い出DataBinder/WebDataBinder

用した場合Spring、データバインディングモジュールは非常に精通していないです(私の以前の記事を無視することができます読んだことがある)、それを埋めるためにお勧めします。

  1. 【スモールトーク】春に結合ホームスプリングデータ---のDataBinder神(ソースコード分析)
  2. --- WebDataBinder、ServletRequestDataBinder、WebBindingInitializerをデータバインディングで[世間話]春の家庭の春...

DataBinderクラスは、データバインディングと呼ばれ、でているorg.springframework.validationこのパッケージは、我々はしっかりと一緒に置か春データバインディングとデータの検証ことがわかりますし、内部弱体化データ検証概念と論理(はSpring発信者が気にしないしましょうデータチェックの詳細は、すべて自動的に)使用のコストを削減するために、それによって行わ。

我々はことを知ってDataBinder、それがメインの外部提供bind(PropertyValues pvs)validate()方法を、当然のことながら、関連するバインディング/検証エラーの対処(コンフィギュレーション)コンポーネント:

public class DataBinder implements PropertyEditorRegistry, TypeConverter {
    ...
    @Nullable
    private AbstractPropertyBindingResult bindingResult; // 它是个BindingResult
    @Nullable
    private MessageCodesResolver messageCodesResolver;
    private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
    // 最重要是它:它是org.springframework.validation.Validator
    // 一个DataBinder 可以持有对个验证器。也就是说对于一个Bean,是可以交给多个验证器去验证的(当然一般都只有一个即可而已~~~)
    private final List<Validator> validators = new ArrayList<>();

    public void bind(PropertyValues pvs) {
        MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues ? (MutablePropertyValues) pvs : new MutablePropertyValues(pvs));
        doBind(mpvs);
    }
    ...
    public void validate() {
        Object target = getTarget();
        Assert.state(target != null, "No target to validate");
        BindingResult bindingResult = getBindingResult();

        // 拿到所有的验证器  一个个的对此target进行验证~~~
        // Call each validator with the same binding result
        for (Validator validator : getValidators()) {
            validator.validate(target, bindingResult);
        }
    }
}

DataBinderこれら2個の非常に別々の提供方法原子を:+検証を結合。彼らは〜+データ検証を結合我々のデータを完了するのに合わせ、フルサービスの独立しました

多くのオンライン記事はと言わありDataBinder〜それは組み合わせロジックのこの部分を扱っていないので、データバインドこの文をチェックし続けるためには、そう正確ではないが完了した後に、それが唯一のオリジナルの容量を提供

Spring MVC処理パラメータのタイミング

Spring MVCロジックに処理パラメータが、彼は花の前で偉大な長さで話し、非常に複雑であり、Spring MVCプロセッサの戻り値:HandlerMethodReturnValueHandler参照:
[小]春のホームSpring MVCのWebコンテナの9つの要素の詳細--- --- HandlerAdapterソースあなたは、戻り値のプロセッサHandlerMethodReturnValueHandlerで記事を読みます

同様に、物品は、に焦点を当てて@RequestBody説明するため〜参照このタイプの
パラメータのプロセッサによって処理:HandlerMethodArgumentResolver処理@RequestBody:最終用途の種類を達成するためにありRequestResponseBodyMethodProcessorSpringプロセッサによってメッセージコンバータのシリーズを完了するために、データバインディング、データチェックなど〜

RequestResponseBodyMethodProcessor

上記の推奨にこのクラスには、見知らぬ人であるべき処理MVCの戻り値の記事のは、それを述べていた:それは扱うことができます@ResponseBody(その参照してください注釈戻り値をsupportsReturnType()法〜)BR />それは別の能力を持っている:それはリクエストパラメータを処理することができる(もちろん、 `マーク@ RequestBody`〜それ)
それは、戻り値の処理の両方があるようにHandlerMethodReturnValueHandler、プロセスのパラメータがありますHandlerMethodArgumentResolverだから、それが命名されたProcessor代わりにResolver/Handler〜このネーミングの芸術である、まあ

// @since 3.1
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestBody.class);
    }
    // 类上或者方法上标注了@ResponseBody注解都行
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));
    }

    // 这是处理入参封装校验的入口,也是本文关注的焦点
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

        // 它是支持`Optional`容器的
        parameter = parameter.nestedIfOptional();
        // 使用消息转换器HttpInputMessage把request请求转换出来
        // 此处注意:比如本例入参是Person类,所以经过这里处理会生成一个空的Person对象出来(反射)
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());

        // 获取到入参的名称
        // 请注意:这里的名称是类名首字母小写,并不是你方法里写的名字。比如本利若形参名写为personAAA,但是name的值还是person
        String name = Conventions.getVariableNameForParameter(parameter);

        // 只有存在binderFactory才会去完成自动的绑定、校验~
        // 此处web环境为:ServletRequestDataBinderFactory
        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);

            // 显然传了参数才需要去绑定校验嘛
            if (arg != null) {

                // 这里完成数据绑定+数据校验~~~~~(绑定的错误和校验的错误都会放进Errors里)
                // Applicable:适合
                validateIfApplicable(binder, parameter);

                // 若有错误消息hasErrors(),并且仅跟着的一个参数不是Errors类型,Spring MVC会主动给你抛出MethodArgumentNotValidException异常
                // 否则,调用者自行处理
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }

            // 把错误消息放进去 证明已经校验出错误了~~~
            // 后续逻辑会判断MODEL_KEY_PREFIX这个key的~~~~
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }

        return adaptArgumentIfNecessary(arg, parameter);
    }

    // 校验,如果合适的话。使用WebDataBinder,失败信息最终也都是放在它身上~  本方法是本文关注的焦点
    // 入参:MethodParameter parameter
    protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
        // 拿到标注在此参数上的所有注解们(比如此处有@Valid和@RequestBody两个注解)
        Annotation[] annotations = parameter.getParameterAnnotations();
        for (Annotation ann : annotations) {
            // 先看看有木有@Validated
            Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);

            // 这个里的判断是关键:可以看到标注了@Validated注解 或者注解名是以Valid打头的 都会有效哦
            //注意:这里可没说必须是@Valid注解。实际上你自定义注解,名称只要一Valid开头都成~~~~~
            if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
                // 拿到分组group后,调用binder的validate()进行校验~~~~
                // 可以看到:拿到一个合适的注解后,立马就break了~~~
                // 所以若你两个主机都标注@Validated和@Valid,效果是一样滴~
                Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
                Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
                binder.validate(validationHints);
                break;
            }
        }
    }
    ...
}

本論文では、唯一の懸念に焦点を当て@Valid、このデータ検証の使用に関連するいくつかの小さな詳細は以下に要約として、があります。

  1. (パラメータ@RequestBody上院にマークされている)のname(形参名)とエンティティクラス名はクラス名の最初の文字を小文字である場合は、書いたものには無関係。(などの配列、コレクション、場合、自分の特定の名前を持つことになります)
  2. @Validatedそして、@Validそれを有効にするのチェックをすることができ、それだけの行に2人の兄弟されていません:任意の名前で始まる「有効」である注釈が施行されたデータの検証を行うことができます
    1. カスタム注釈名はValidで始まる、とにvalue同じ指定のプロパティが可能なGroupパケットを
    2. 直接個人が使用することをお勧めします@Validated、使用しようとすることができます@Validどのような〜のカスタム注釈にしていない自分自身のためのトラブルに、
  3. 場合にのみ、Errors(BindingResult)引数が続くだけされ@Valid注釈付きエンティティを、Spring MVCエラーメッセージ入れます呼び出し側に地方分権化プロセスを(または次のではないではない)、それはスローそうでない場合、MethodArgumentNotValidException例外を-

これは、使用して行われる@RequestBodyの組み合わせ@Validデータ検証のための基本的な原則を。実際には、ときSpring MVCの処理@RequestPart時間パラメータは、ノートによると、関連する論理的な結合を実装します、確認してください。対応するプロセッサRequestPartMethodArgumentResolver、及びこの原理は、主に、処理とほぼ同様であるMultipart〜本明細書の無視、関連


==ここでのポイントをヒント、記事好奇心の小さな赤ちゃんの後に送信され、私に尋ねた上院にとして複数のオブジェクトを使用することができます@RequestBodyそれをマーク?==
この問題についてのが合理的と考えるかないようにしましょう、私たちはこれを試してください:

    @PostMapping("/hello")
    public Object helloPost(@Valid @RequestBody Person personAAA, BindingResult result, @Valid @RequestBody Person personBBB) {
        ...
    }

リクエストが与えられていました。

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed]

エラーメッセージはよく理解されている:そのボディ要求フィールドは、(一度だけまあストリーミング読み取ることができます)一度だけ読み込まれます。
あなたが興味があれば、あなたも頼むかもしれない:URLパラメータを?リクエストリンク?その背後にあるパラメータを、どのようにパッケージ化するには?この記事の焦点は、興味を持った場合に行ってください〜左、の一部ではないので

注意:その使用マップ、リスト、配列、のために受け入れられた@RequestBody地図のバインダーは、リストは、以前の記事をチェックすることを除いて、同様の状況パラメータは、ここで行われていない、説明しなければなりませんでした。

私はそれがユーザーとのために、より重要であるため、読者は、この部分を習得することを願って@InitBinder強い関連~~~

一般的に、実際の使用において用いられる@Validated〜エラーメッセージ優しい発信者を表示するために、パケットのチェックサム(必要な場合)、および、グローバル例外処理モードと組み合わせます

グローバル例外ハンドラの例

チェックが失敗すると、SpringそれがスローされますMethodArgumentNotValidException、チェック結果オブジェクトを保持するた、例外をBindingResult検証障害情報を取得します。例示のみの目的のために現時点で唯一:

@RestControllerAdvice
public class MethodArgumentNotValidExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();

        StringBuilder stringBuilder = new StringBuilder();
        for (FieldError error : bindingResult.getFieldErrors()) {
            String field = error.getField();
            Object value = error.getRejectedValue();
            String msg = error.getDefaultMessage();
            String message = String.format("错误字段:%s,错误值:%s,原因:%s;", field, value, msg);
            stringBuilder.append(message).append("\r\n");
        }
        return Result.error(MsgDefinition.ILLEGAL_ARGUMENTS.codeOf(), stringBuilder.toString());
    }
}

レガシー痛みのポイント

あなたはその中で見つけてくださいSpring MVC:私たちにデータを確認するために非常に便利な方法を提供しますが、それはまだ比較的大きな制限があることを検証すべきパラメータは、JavaBeanのですが必要です

ご注意:なく、同じ要件が、その後、まだ検証を有効にすることができ、受信JavaBeanの場合は上院への要求を取得するように、ああ、ボディ本体を要求しています

しかし、実用的なアプリケーションでは、実際には、我々は非常に多くあるController引数の方法である平铺的、いわゆる平铺参数このような形、:

    @PutMapping("/hello/id/{id}/status/{status}")
    public Object helloGet(@PathVariable Integer id, @PathVariable Integer status) {
        ...
        return "hello world";
    }

実際には、特に場合は、要求を取得し@RequestParam、一般的に上院には、人間の肉を通じて非常に多く(例えば、ページングクエリなど)、タイル張りのパラメータは、このような場合のためのもの、私たちは実際にあるであるif elseことを確認しに行くには?
おそらく、あなたは、この問題に関心を持っている、そして今、この記事を読んで、それはあなたのソリューションを与えることができます:
[春]小さなホームコントローラは、タイルパラメータのデータ検証の実装をサポートします(@Validを使用してSpring MVCのデフォルトは唯一のJavaBeanすることができチェックしてください)

== @検証済みと==の間@Valid違い

問題のタイトルとして、私はそれがあると信じて懸念している多くの小さなパートナーの比較あなたはこのシリーズが持っていたニャーを置けば、この質問に対する答えが表面化:

  1. @Valid:検証の戻り値、再帰的なカスケードおよびチェックをタグ付けするために使用されるJSR-303標準仕様注釈タグタイプ、プロパティおよびメソッド
  2. @ValidatedSpringアノテーションが標準であるJSR-303、変異体(サプリメント)のグループ化機能を提供し、パラメータが異なるグループ化に基づいて、異なる認証メカニズムを使用時に検証することができます
  3. Controller@Validと@Validatedない特定の差を使用して検証方法の時間パラメータ(パケットチェックサム・ワードを必要に応じて)
  4. @Validatedアノテーションは、クラスレベルのばねレベルの較正パラメータをサポートするための方法を使用することができます。@Valid属性レベルの制約を表すために使用することができる連結チェックを
  5. @Validatedクラス、メソッド、およびパラメータのみを使用することができ、@Validそれは方法で使用することができるフィールド、コンストラクタ、すべての値

ヒント最後のポイント:Spring Bootザ・がWeb Starter参加しているBean Validationだけでなく、実装依存は、直接使用することができます。しかし、それが純粋であればSpring MVC、環境、自己紹介〜

概要

使用:この記事では、我々は通常、データ検証シナリオを使用するほとんどについてです@Validated完了するには、Controllerエレガントなプロセスデータの検証を達成するために、上院にチェックを。私はこの記事はあなたが完全に理解します願っています@Validated和@Valid〜実際の生産使用で、より便利行うことができ、使用上の差異との接触を

知識交換

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

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

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

おすすめ

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