Spring Bootアプリケーションでリクエストデータの妥当性を検証する際、@Validと@Validatedの2つの主要アノテーションが使用されます。両者は共通点がありますが、使用シナリオや実装メカニズムに明確な違いがあります。
アノテーションの核心的差異
- パッケージ位置:
@Validはjavax.validationパッケージに、@Validatedはorg.springframework.validationパッケージに存在します - 適用対象:
@Validはフィールド・メソッド戻り値・パラメータなど幅広い対象に適用可能ですが、@Validatedはクラスレベルまたはメソッドパラメータに限定されます - 検証実装:
@ValidはJSR-380標準規格のHibernate Validatorを基盤とし、@ValidatedはSpring独自の検証ラッパーを経由して実装されます - 分組機能:
@Validatedは検証グループの指定が可能で、リクエストコンテキストに応じた動的検証が実現できます
主要検証アノテーション一覧
| アノテーション | 機能説明 |
|---|---|
@Email | メールアドレス形式の妥当性検証 |
@Size | 文字列・コレクションの長さ制約 |
@NotBlank | 空白文字を含まない文字列の検証 |
@Pattern | 正規表現による文字列マッチング |
@Min/@Max | 数値の上限・下限チェック |
@DecimalMin | 小数点付き数値の下限チェック |
@Future | 未来日付の検証 |
依存関係設定
Spring Bootプロジェクトではspring-boot-starter-validationを追加することで、Bean Validation APIとHibernate Validatorが自動的に設定されます。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>実装例: ユーザー登録検証
検証グループを定義したDTOクラスを作成します。各フィールドに適切なアノテーションを付与し、グループごとに検証ルールを分離します。
@Data
public class UserRegistrationRequest {
@Email(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
groups = RegistrationValidation.class)
private String emailAddress;
@Length(min = 2, max = 10,
groups = {RegistrationValidation.class, AuthValidation.class})
private String userName;
@Length(min = 8, groups = RegistrationValidation.class)
private String userPassword;
@Length(min = 6, max = 6, groups = RegistrationValidation.class)
private String verificationCode;
}コントローラーで検証を実行する際、@Validatedに検証グループを指定します。
@RestController
public class UserController {
@PostMapping("/register")
public ResponseEntity<?> register(
@RequestBody @Validated(RegistrationValidation.class)
UserRegistrationRequest request
) {
// キャッシュからの検証コード確認処理
Cache<String> codeCache = verificationCache.get(request.getEmailAddress());
if (codeCache == null || codeCache.isExpired() ||
!codeCache.getValue().equals(request.getVerificationCode())) {
return ResponseEntity.badRequest().body("無効な認証コードです");
}
// ユーザー登録ロジック実行
return userService.register(request);
}
}エラーハンドリング戦略
検証エラーを一括で処理するためのグローバル例外ハンドラーを実装します。エラーメッセージをJSON形式で返却する標準的な実装例です。
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationErrors(
MethodArgumentNotValidException ex
) {
Map<String, String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.toMap(
FieldError::getField,
FieldError::getDefaultMessage
));
return ResponseEntity.badRequest().body(errors);
}
}メソッドレベルでBindingResultパラメータを直接受け取る方法もあります。この場合、検証結果を手動で処理する必要があります。
@PostMapping("/login")
public ResponseEntity<?> login(
@RequestBody @Valid AuthRequest request,
BindingResult result
) {
if (result.hasErrors()) {
// エラー処理ロジック
}
// 正常処理
}