プログラミング逆引き辞典

~ 多言語対応のプログラミングレシピ ~

SpringBoot Beanバリデーションの作成方法

アノテーションを自作して独自のBeanバリデーションを作成する方法
 
 

①アノテーションクラスの作成

【基本構文】

//アノテーションで付加された情報がどの段階まで保持されるかを定義
@Retention(リテンション名)
//アノテーションを付与できる対象を指定
@Target(ターゲット名)
// javadoc コマンドなどで作成したドキュメントに反映させる為の設定
@Documented
//バリデーションを行うクラスを指定
@Constraint(validatedBy = 実際にバリデーションを行うクラス名.class)
public @interface アノテーション名 {
    //フィールドの設定(メソッドのようにカッコを付与 default値の設定が可能)
    int maxLength() default 5;
    ・
    ・
    //特定のバリデーショングループをカスタマイズ可能にする設定(※空の Class<?> 型で初期化)
    Class<?>[] groups() default {};
    //チェック対象のオブジェクトにメタ情報を与える為の宣言
    Class<? extends Payload>[] payload()  default {};
    //エラー時に例外オブジェクトに設定されるメッセージ
    String message() default "";
}

 
 


【Retentionアノテーションの種類】

・SOURCE
ソース上だけのアノテーションで、クラスファイルには記録されない
 
・CLASS
クラス・ファイルに記録され、実行時にVMによって保持されない
 
・RUNTIME
クラス・ファイルに記録、実行時にVMによって保持され、リフレクション可能
 
 


【Targetアノテーションの種類】

・ElementType.TYPE
クラス・インタフェース・enum・アノテーションの宣言
 
・ElementType.FIELD
フィールドの宣言(enum定数を含む)
 
・ElementType.METHOD
メソッドの宣言
 
・ElementType.PARAMETER
メソッドのパラメータの宣言
 
・ElementType.CONSTRUCTOR
コンストラクタの宣言
 
・ElementType.LOCAL_VALIABLE
ローカル変数の宣言
 
・ElementType.ANNOTATION_TYPE
アノテーション型の宣言
 
・ElementType.PACKAGE
パッケージの宣言
 
 


②バリデーションクラスの作成

【基本構文】

//ConstraintValidatorインターフェースを実装
public class バリデーションクラス名 implements ConstraintValidator<Sample, Object>{
    フィールド名
    ・
    ・

    /** アノテーションの情報を受け取る */
    @Override
    public void initialize(アノテーション名 constraintAnnotation) {
        フィールド名 = constraintAnnotation.フィールド名;
        ・
        ・
        message = constraintAnnotation.message();
    }

    @Override
    public boolean isValid(Object obj, ConstraintValidatorContext context) {
        // Beanバリデーションの検証処理を実装
        //エラーの場合はfalseを返す
        return false;
    }

}

 
 

【ConstraintValidatorインターフェース】

ConstraintValidator<アノテーションクラス, バリデーション対象の型>を指定する
 
バリデーション対象の型はString型などを指定すれば良い
FormやDTO全体にバリデーションをかける場合はObject型を指定する
 
 


③実装サンプル

・アノテーションクラス

package com.example.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

import com.example.logic.SampleValidationLogic;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)

//尚、複数の場合は波括弧で括る
//@Target({
//  ElementType.FIELD,
//  ElementType.METHOD
//})

@Documented
@Constraint(validatedBy = SampleValidationLogic.class)
public @interface Sample {
    int maxLength() default 5;

    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String message() default "";

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        //アノテーションを使用するときに、そのフィールド名を省略してパラメータを渡せるようにしておく
        //単一値アノテーションで値のキー名が value の場合のみ、キー名を省略できる
        ByteValidation[] value();
    }
}

 
 
・実際にバリデーションを行うクラス

package com.example.logic;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import com.example.annotation.Sample;

public class SampleValidationLogic implements ConstraintValidator<Sample, Object>{
    private int maxLength;
    private String message;

    /** アノテーションの情報を受け取る */
    @Override
    public void initialize(Sample constraintAnnotation) {
        maxLength = constraintAnnotation.maxLength();
        message = constraintAnnotation.message();
    }

    /** Beanバリデーションの検証処理 */
    @Override
    public boolean isValid(Object obj, ConstraintValidatorContext context) {
        //Beanをラップ
        BeanWrapper wrapper = new BeanWrapperImpl(obj);

        //ラップされたBeanのインスタンスを取得
        Object subObj = wrapper.getWrappedInstance();

        int value = (int)(subObj);

        //5文字より大きければエラーとする
        if (value > maxLength) {
            message = "error";
            return false;
        }

        return true;
    }

}

 
 
・アノテーションを付与するクラス

package com.example.domain;

import com.example.annotation.Sample;

public class SampleEntity {
    //自作のSampleアノテーションを付与
    //サンプルとして数値を10で初期化
    @Sample
    public int len = 10;
}

 
 
・コントローラークラス

package com.example.controller;

import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.domain.SampleEntity;

@RestController
public class SampleController {
    @GetMapping
    //@Validatedでバリデーションチェックし、BindingResultでエラーを受ける
    String index(@Validated SampleEntity sampleEntity, BindingResult result) {
        if (result.hasErrors()) {
            return "error";
        }

        return "success";
    }
}