Skip to main content

JavaBeans Validationの実行方法

feature javabeans validation

2023年5月1日

0 分で読めます

JavaBeans Validation(Bean Validation)は、Java Enterprise Edition 6プラットフォームの登場以来利用できる検証モデルです。さまざまなJavaアプリケーションで入力データを検証するための標準的な方法を提供します。以前の記事では、Java DTOを使って、最新のアプリケーションで意図しないデータ漏えいを防ぐ方法について説明しました。

この記事では、新しいJavaアプリケーションを作成し、サンプル文字列を検証して、JavaBean Validationの仕組みを説明します。まず、Beanとは何かをおさらいしましょう。 

JavaBeansは、Javaアプリケーションのさまざまな部分間でデータを保存、転送するためのプロパティとメソッドを含むJavaオブジェクトです。Beanは、Javaアプリケーションのデータと動作をカプセル化した再利用可能なコンポーネントです。特定の設計パターンと規約に従い、オブジェクトを表現する標準的な方法です。 

  • JavaBeansにはプロパティが含まれます。プロパティはオブジェクトの状態を表すインスタンス変数です。これらの変数はprivateにする必要があります。Beanに保存されたデータへのアクセスには、getterメソッドとsetterメソッドを使用します。

  • Beanのインスタンスを作成するには、引数なしのコンストラクターを使用します。コンストラクターを追加すれば、Beanをさまざまな方法で初期化できます。

  • JavaBeansはシリアライズ可能です。つまり、その状態をファイルに保存したり、ネットワーク経由で送信したりできます。

  • JavaBeansには命名規則があり、プロパティの値へのアクセスや設定にはgetXXXメソッドとsetXXXメソッドを使用します。

  • JavaBeansには、ツールやIDEにBeanの情報を提供するBeanInfoクラスが含まれていることもよくあります。

JavaBeansは、大規模なアプリケーションのデータを整理し、構造化するための構成要素としてよく使用されます。通常、インスタンス変数(プロパティ)と、Beanに保存されたデータにアクセスするためのgetterメソッドおよびsetterメソッド(getXXXとsetXXX)が含まれます。また、アプリケーションのさまざまな部分で再利用できるカスタムオブジェクトの作成にもJavaBeansを使用できるため、コードの保守や更新が容易になります。

JavaBeans Validationの使用

Beanの主な用途の1つは、Beanのプロパティに制約を指定してユーザー入力を検証することです。たとえば、最小長と最大長、使用可能な文字、必須フィールドなどを指定できます。これらの制約を使ってユーザーが入力したデータを検証し、アプリケーションで処理する前に、指定した要件を満たしていることを確認します。

次の例では、Hibernate ValidatorをApache Mavenプロジェクトで使用します。Hibernate Validatorは、Bean Validationの参照実装です。

Javaアプリの作成

まず、Mavenのpom.xmlファイルにHibernate Validatorの依存関係を追加します。次のコードを使用してください。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.5.Final</version>
</dependency>

次に、以下のコードを使って、Bean Validation APIを依存関係としてpom.xmlに追加します。

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

続いて、BeanValidation.javaという新しいファイルにJavaプロジェクトを作成し、必要なライブラリをインポートします。

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.constraints.Pattern;
import java.util.Set;
import javax.validation.ConstraintViolation;

次のコードを使って、検証対象のプロパティを持つシンプルなBeanを作成します。

public class ExampleBean {
    @NotNull
    @Size(min=2, max=10)
    private String exampleString;

//Getters
    public String getExampleString() {
        return exampleString;
    }

//Setters
    public void setExampleString(String exampleString) {
        this.exampleString = exampleString;
    }
}

この例では、@NotNullと@Sizeのアノテーションを使い、exampleStringプロパティがnullではなく、最小文字数が2文字、最大文字数が10文字であることを指定しています。ExampleBeanクラスでは、getExampleStringメソッドとsetExampleStrinメソッドを、exampleStringプロパティのgetterとsetterとして定義します。

Bean Validationの仕組みを確認する

BeanValidationクラスを使ってBeanを検証するには、次のコードを実行します。

public class BeanValidation {
    public static void main(String[] args) {
        ExampleBean bean = new ExampleBean();
        bean.setExampleString("hello");
// Create a Validator instance using the default validation factory      
  ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
// Validate an instance of the ExampleBean class and get a set of constraint violations, if any        
Set<ConstraintViolation<ExampleBean>> violations = validator.validate(bean);
        if (!violations.isEmpty()) {
            System.out.println("Errors:");
            for (ConstraintViolation<ExampleBean> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("Validation successful");
        }
    }
}

文字列の値をhelloに設定しているため、検証に合格します。exampleStringプロパティに、Beanで指定した制約を満たさない値を設定すると、validateメソッドはエラーの内容を示すConstraintViolationオブジェクトのセットを返します。

文字列が検証に失敗した場合のエラーを処理するには、違反を確認し、違反があればエラーメッセージを表示するエラー処理を作成します。

BeanValidation.javaファイルのコード全体を以下に示します。

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.ConstraintViolation;

public class ExampleBean {
    @NotNull
    @Size(min=2, max=10)
    private String exampleString;

    public String getExampleString() {
        return exampleString;
    }

    public void setExampleString(String exampleString) {
        this.exampleString = exampleString;
    }
}

public class BeanValidation {
    public static void main(String[] args) {
        ExampleBean bean = new ExampleBean();
        bean.setExampleString("hello");

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        Set<ConstraintViolation<ExampleBean>> violations = validator.validate(bean);

        if (!violations.isEmpty()) {
            System.out.println("Errors:");
            for (ConstraintViolation<ExampleBean> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("Validation successful");
        }
    }
}

この例では、入力文字列が2文字未満、10文字超、またはnullの場合にエラーを返します。文字列がこれらの制約を満たさない場合、満たしていない要件を示すエラーメッセージを出力します。

メールアドレスとパスワードのフィールドを使って、ユーザーのログイン入力を検証する制約の例を見てみましょう。

public class UserBean {
    @NotNull
    @Size(min=5, max=30)
    @Pattern(regexp = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}")
    private String email;

    @NotNull
    @Size(min=8, max=15)
    @Pattern(regexp = "((?=.*[a-z])(?=.*\\d)(?=.*[A-Z])(?=.*[@#$%!]).{8,15})")
    private String password;

}

UserBeanクラスには、emailとpasswordの2つのプロパティがあります。emailプロパティでは、入力が5文字以上30文字以下で、有効なメールアドレスの形式に一致することを確認します。 

passwordプロパティでは、パスワードがnullではなく、8文字以上15文字以下で、小文字、大文字、数字、特殊文字をそれぞれ1文字以上含むことを確認します。 

制約について

制約はアノテーションを使って定義します。アノテーションは、Javaのクラス、フィールド、メソッドに追加する特別な種類のメタデータです。Beanクラスのプロパティには、プロパティ自体、プロパティのgetterメソッド、またはクラスのコンストラクターパラメーターなど、さまざまな場所に制約を適用できます。

Beanに制約を定義したら、Bean Validation APIのValidatorオブジェクトを使って検証します。Validatorオブジェクトは、Beanのインスタンスがプロパティに設定された制約を満たしているかを確認します。Beanが制約に違反している場合、Validatorは検証に失敗したプロパティとその理由を示す制約違反のセットを返します。

JavaBeansには、Bean Validation APIの一部として標準で利用できる組み込み制約があり、すぐに使用できます。組み込み制約には、@NotNull、@Size、@Pattern、@Min、@Max、@DecimalMin、@DecimalMaxなどがあります。組み込み制約の一覧については、Hibernate Validatorのドキュメントをご覧ください。

カスタム制約とは、特定の検証要件を満たすために定義する制約です。カスタムアノテーションを定義し、検証ロジックを定めるConstraintValidatorを実装して作成します。 

カスタムアノテーションの作成

Bean Validation用のカスタム制約を作成するには、次の手順を実行します。

  • カスタムアノテーションクラスを作成し、@Constraintを付けます。これにより、アノテーションを付けたクラスがカスタム制約であることを示します。

  • ConstraintValidatorインターフェースを実装するクラスで、検証ロジックを実装します。このクラスには、カスタム制約アノテーションと、検証可能な要素の型をパラメーターとして指定する必要があります。

  • 検証用XML設定ファイルにエントリーを追加するか、カスタム制約クラスに@Constraint(validatedBy=<Validator class>)を付けて、カスタム制約を登録します。

ValidSearchTermという名前のカスタム制約の例を見てみましょう。

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@Documented
@Constraint(validatedBy = ValidSearchTermValidator.class)
@Target({ FIELD })
@Retention(RUNTIME)
public @interface ValidSearchTerm {

    String message() default "Search term contains invalid characters";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

続いて、正規表現を使った検証ロジックを実装し、制約を作成します。この例では、特殊文字を含まない一般的な英数字の検索文字列を指定します。

public class ValidSearchTermValidator implements ConstraintValidator<ValidSearchTerm, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        return value.matches("[A-Za-z0-9]+");
    }
}

次に、以下のようにBeanでカスタム制約を使用します。

public class SearchBean {
    @ValidSearchTerm
    private String searchTerm;

    public String getSearchTerm() {
        return searchTerm;
    }

    public void setSearchTerm(String searchTerm) {
        this.searchTerm = searchTerm;
    }
}

Bean Validationのまとめ

Bean Validationは、Javaアプリケーションのデータを統一的かつ一貫した方法で検証できるため、安全なデータ処理を実現するコードの作成と保守が容易になります。Bean Validationモデルを使えば、カスタムの検証ロジックを書くために費やしていた時間と労力を削減できます。

Bean Validationでは、アノテーションを使ってJavaBeanのプロパティに制約を指定できます。アノテーションには組み込みのものとカスタムのものがあり、ユーザー入力が有効と見なされるための要件を定義します。このモデルは、制約を使ってBeanを検証し、エラーを特定します。

Bean Validationでは、検証中に発生したエラーを処理する方法も提供されます。フレームワークは制約違反のリストを返すため、その情報を使ってユーザーに具体的なフィードバックを提示できます。これにより、ユーザー体験が向上し、より堅牢で安全なアプリケーションを実現できます。

総じて、Bean ValidationはJavaアプリケーションの品質とセキュリティを確保するために欠かせないツールです。このモデルのベストプラクティスに従うことで、適切に設計され、保守しやすく、安全なアプリケーションを実現できます。

CTFを始めよう

オンデマンドのバーチャル入門ワークショップで、CTFチャレンジの解き方を学びましょう。