0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring Boot カスタムバリデーションアノテーション

Last updated at Posted at 2025-09-02

Spring Boot: カスタムバリデーションで複数フィールドをまとめてチェックしよう!

なんでカスタムアノテーション?

「パスワード変更で新旧パスワードが同じだったらエラーにしたい」
「確認用パスワードが一致しないときエラーにしたい」

こんなとき、標準の@NotNull@Sizeじゃ対応できませんよね。
でも大丈夫!自分でアノテーション作れば複数のフィールドをまとめてチェックできちゃいます。

今回作るもの

パスワード変更で「現在のパスワード」と「新しいパスワード」が同じだったらエラーにする
@DifferentPasswordsアノテーションを作ります!

@DifferentPasswords // ← これで複数フィールドをチェック!
public class ChangePasswordCommand {
    private String currentPassword;
    private String newPassword;
}

ステップ 1: アノテーションを作る

まずはアノテーション本体を作ります。たった数行です!

@Target(ElementType.TYPE)  // クラスに付けるアノテーション
@Retention(RetentionPolicy.RUNTIME)  // 実行時まで残す
@Constraint(validatedBy = DifferentPasswordsValidator.class)  // チェック処理はこのクラス
public @interface DifferentPasswords {
    String message() default "新しいパスワードは現在のパスワードと異なる必要があります";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

ここがポイント!

  • @Target(ElementType.TYPE): フィールド 1 個じゃなくてクラス全体にアノテーションを付ける
  • @Retention(RUNTIME): Spring Boot が実行時に見つけられるようにする
  • @Constraint: 実際のチェック処理を書くクラスを指定

この設定で「複数フィールドをまとめてチェック」が可能になります!

ステップ 2: 実際のチェック処理を書く

次に、実際にパスワードをチェックする処理を書きます。

public class DifferentPasswordsValidator implements ConstraintValidator<DifferentPasswords, ChangePasswordCommand>{

    @Override
    public boolean isValid(ChangePasswordCommand command, ConstraintValidatorContext context){
        // どちらかがnullなら、とりあえずOK(@NotNullに任せる)
        if(command.getCurrentPassword() == null || command.getNewPassword() == null){
            return true;
        }

        // ここが肝心!複数フィールドを比較
        return !command.getCurrentPassword().equals(command.getNewPassword());
    }
}

すごいところ

  • ChangePasswordCommandオブジェクト全体が渡されるので、好きなフィールドにアクセスし放題!
  • getCurrentPassword()getNewPassword()両方ともチェックできる
  • 戻り値falseでエラー、trueで OK

これで複数フィールドの関係性をチェックする準備完了です!

ステップ 3: 使ってみる!

作ったアノテーションをクラスに付けるだけ!

@DifferentPasswords  // ← ここに付ける!
public class ChangePasswordCommand {
    @NotNull(message = "現在のパスワードは必須です")
    private String currentPassword;

    @NotNull(message = "新しいパスワードは必須です")
    @Size(min = 8, max = 100, message = "新しいパスワードは8文字以上100文字以内で入力してください")
    private String newPassword;

    // getter, setter...
}

コントローラーでは普通に@Validするだけ!

@PostMapping("/change-password")
public ResponseEntity<?> changePassword(@Valid @RequestBody ChangePasswordCommand command) {
    // バリデーション成功時の処理
    return ResponseEntity.ok().build();
}

これだけで、リクエスト受信時に自動的に複数フィールドをチェックしてくれます!

実際の動作確認

例えば、こんな JSON が POST されたとき:

{
  "currentPassword": "oldpassword123",
  "newPassword": "oldpassword123" // 同じパスワード!
}

自動的にDifferentPasswordsValidatorが動いて、エラーレスポンスが返されます:

{
  "error": "新しいパスワードは現在のパスワードと異なる必要があります"
}

応用例:他にもこんなチェックができる

パスワード確認チェック

@PasswordMatches
public class RegisterCommand {
    private String password;
    private String confirmPassword;
}

日付の前後関係チェック

@StartDateBeforeEndDate
public class EventCommand {
    private LocalDate startDate;
    private LocalDate endDate;
}

住所の整合性チェック

@ValidAddress
public class AddressCommand {
    private String prefecture;
    private String city;
    private String zipCode;
}

なぜカスタムアノテーションが便利?

🎯 複数フィールドを一度にチェック

標準アノテーションは 1 フィールドずつしかチェックできないけど、カスタムならオブジェクト全体を見れる!

🔄 再利用できる

一度作れば、同じパターンのチェックは使い回し放題

📖 コードが読みやすい

@DifferentPasswordsって書いてあるだけで何をチェックしてるか一目瞭然

🛠️ Spring Boot と完全統合

@Validするだけで自動的に動く。特別な設定不要!

まとめ

たった 2 つのクラスを作るだけで、複数フィールドの関係性をチェックできる強力なバリデーションが作れます!

  1. アノテーション定義(@Target(ElementType.TYPE)がポイント)
  2. バリデータクラス(オブジェクト全体にアクセス可能)
  3. 使いたいクラスに付けるだけ

標準アノテーションでは無理だった「フィールド同士の比較」が簡単にできちゃいます。ぜひ試してみてください!

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?