Getting started with JSR 303 (Bean Validation) – part 2

Creating custom constraint annotations

カスタムバリデーション用アノテーションの作成を説明します。


まず、カスタムのアノテーションを定義します。
アノテーションには以下のアノテーションを付加する必要があります。

・@Target( { METHOD, FIELD, ANNOTATION_TYPE }) :アノテーションを付加する対象をメソッド、フィールド、アノテーションとして指定します。
・@Retention(RUNTIME):バイトコードアノテーションを含めます。
・@Constraint(validatedBy = MaxValidator.class):アノテーションの検証用クラスを指定します。検証用クラスは後ほど説明します。


次に必須のアノテーションプロパティを定義します。
String message() default "{validator.max}"; String型のmessageプロパティを定義します。 デフォルトはValidationMessages.propertiesファイルのキーで定義するのを推奨します。プロパティファイルのキーは"{}"で定義します。


Class[] groups() default {}; Classの配列型でgroupsプロパティを定義します。デフォルトは空でよいですが、Defaultインターフェースが内部的に適用されます。


※当初の仕様ではClassではなくString型での仕様が提案されていました。確かにClassの方がタイプセーフに記述できたり、集約できたりとメリットもあるのですが、私としてはグループに対していちいちクラス(インターフェース)を一つ一つ定義するのは冗長に感じてしまいます。また、Classの場合は動的にグループを定義することに対しては難しいのですが、逆にStringの場合だと容易です。Classの場合とStringの場合とではそれぞれ一長一短なのですが個人的にはStringが好みです。


Click framework のアクションリスナ、URLでのメソッドリゾルブを考えるとStringの方が他のフレームワークとの連携もしやすいだろうと考えるのもその理由です。

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

@Target( { METHOD, FIELD, ANNOTATION_TYPE }) //アノテーションを付加する対象を指定
@Retention(RUNTIME)
@Constraint(validatedBy = MaxValidator.class) //検証用クラスの指定
public @interface Max {

    String message() default "{validator.max}"; //メッセージプロパティ

    Class<?>[] groups() default {}; //グループプロパティ

  long max(); //独自のプロパティ

}

次に、検証用のクラスを定義します。まず、ConstraintValidatorインターフェースを実装します。
""のようにパラメータに先ほど定義したアノテーション、検証値のクラスタイプを順に定義します。


処理は最初に"initialize"メソッドが呼ばれ、次に"isValid"メソッドが呼ばれます。
そのため独自のプロパティがある場合はかならず"initialize"メソッドにて処理する必要があります。


その後、フレームワークによって"isValid"メソッドが呼ばれますが、検証値が不正な場合はfalseを返却する
用にします。そうでない場合は正常として処理されます。


検証用のクラスの記述が完了したら、検証用アノテーションの@Constraint(validatedBy = MaxValidator.class)
に記述します。

public class MaxValidator implements ConstraintValidator<Max, Long> {

  long max;

    public void initialize(Max constraintAnnotation) {
        max = constraintAnnotation.max(); //比較値を初期化する
    }

    public boolean isValid(Long obj,
        ConstraintValidatorContext constraintContext) {

       if (obj > max ){
          return false; //不正な場合はfalseを返す
       } else {
          return true;
       }
    }

}

以上でカスタムアノテーションの作成は完了です。プロパティに付加して利用します。