1概要

この記事では、Bean Validation 2.0(JSR-380)を使ってメソッド制約を定義および検証する方法について説明します。

リンク:/javax-validation[前の記事]では、JSR-380とその組み込みアノテーション、およびプロパティー検証の実装方法について説明しました。

ここでは、次のようなさまざまな種類のメソッド制約に焦点を当てます。

  • 単一パラメータ制約

  • クロスパラメータ

  • 制約を返す

また、Spring Validatorを使用して手動で自動的に制約を検証する方法についても説明します。

次の例では、linkの場合とまったく同じ依存関係が必要です。/javax-validation[Java Bean Validation Basics]。


2メソッド制約の宣言

まず始めに、

メソッドパラメータの制約を宣言し、メソッドの戻り値を返す方法

について説明します。

前述のように、

javax.validation.constraints

の注釈を使用できますが、カスタム制約を指定することもできます(たとえば、カスタム制約またはクロスパラメータ制約の場合)。


2.1. 単一パラメータ制約

単一パラメータに制約を定義するのは簡単です。

必要に応じて各パラメーターに注釈を追加するだけです

:

public void createReservation(@NotNull @Future LocalDate begin,
  @Min(1) int duration, @NotNull Customer customer) {

   //...
}

同様に、コンストラクタにも同じ方法を使用できます。

public class Customer {

    public Customer(@Size(min = 5, max = 200) @NotNull String firstName,
      @Size(min = 5, max = 200) @NotNull String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

   //properties, getters, and setters
}


2.2. クロスパラメータ制約の使用

場合によっては、複数の値を一度に検証する必要があることがあります。たとえば、2つの数値が他方よりも大きいということです。

これらのシナリオでは、2つ以上のパラメーターに依存する可能性があるカスタムのクロスパラメーター制約を定義できます。

  • クロスパラメータ制約は、クラスレベルの制約** と同等のメソッド検証と見なすことができます。両方を使用して、いくつかのプロパティに基づく検証を実装できます。

簡単な例を考えてみましょう。前のセクションの

createReservation()

メソッドのバリエーションは、__LocalDate型の2つのパラメータ、すなわち開始日と終了日を取ります。

したがって、

begin

が将来、

end



begin

の後にくるようにします。前の例とは異なり、単一パラメータの制約を使用してこれを定義することはできません。

代わりに、クロスパラメータ制約が必要です。

単一パラメータ制約とは対照的に、

クロスパラメータ制約はメソッドまたはコンストラクタ

で宣言されます。

@ConsistentDateParameters
public void createReservation(LocalDate begin,
  LocalDate end, Customer customer) {

   //...
}


2.3. クロスパラメータ制約の作成


@ ConsistentDateParameters

制約を実装するには、2つのステップが必要です。

まず、制約アノテーションを** 定義する必要があります。

@Constraint(validatedBy = ConsistentDateParameterValidator.class)
@Target({ METHOD, CONSTRUCTOR })
@Retention(RUNTIME)
@Documented
public @interface ConsistentDateParameters {

    String message() default
      "End date must be after begin date and both must be in the future";

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

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

ここでは、これら3つのプロパティは制約アノテーションに必須です。


  • message –

    は、エラーメッセージを作成するためのデフォルトキーを返します。

メッセージ補間を使用できるようにします
**

groups

– 制約の検証グループを指定することができます


  • payload

    – Bean Validation APIのクライアントによって使用されます。

カスタムペイロードオブジェクトを制約に割り当てる

カスタム制約を定義する方法の詳細については、https://docs.jboss.org/hibernate/stable/validator/reference/en/us/html__single/#validator-customerconstraintsを参照してください。

その後、バリデータクラスを定義できます。

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ConsistentDateParameterValidator
  implements ConstraintValidator<ConsistentDateParameters, Object[]> {

    @Override
    public boolean isValid(
      Object[]value,
      ConstraintValidatorContext context) {

        if (value[0]== null || value[1]== null) {
            return true;
        }

        if (!(value[0]instanceof LocalDate)
          || !(value[1]instanceof LocalDate)) {
            throw new IllegalArgumentException(
              "Illegal method signature, expected two parameters of type LocalDate.");
        }

        return ((LocalDate) value[0]).isAfter(LocalDate.now())
          && ((LocalDate) value[0]).isBefore((LocalDate) value[1]);
    }
}

ご覧のとおり、

isValid()

メソッドには実際の検証ロジックが含まれています。まず、

LocalDate型の2つのパラメータを取得するようにします。その後、両方が未来で、

end



begin__の後にあるかどうかを確認します。

また、

ConsistentDateParameterValidator

クラスの

@ SupportedValidationTarget(ValidationTarget





PARAMETERS)

アノテーションが必要であることに注意することも重要です。これは、

@ ConsistentDateParameter

がメソッドレベルで設定されているためですが、制約はメソッドパラメータに適用される必要があります(次のセクションで説明するように、メソッドの戻り値には適用されません)。

注意:Bean Validation仕様では、

null

-valuesを有効なものと見なすことを推奨しています。

null

が有効な値ではない場合は、代わりに

@ NotNull

アノテーションを使用してください。

** 2.4. 戻り値の制約

  • メソッドから返されるオブジェクトを検証する必要がある場合があります。このために、戻り値の制約を使うことができます。

次の例では組み込み制約を使用しています。

public class ReservationManagement {

    @NotNull
    @Size(min = 1)
    public List<@NotNull Customer> getAllCustomers() {
        return null;
    }
}


getAllCustomers()

には、以下の制約が適用されます。

  • まず、返されるリストは

    null

    であってはいけません。

エントリ
** さらに、リストに

null

個のエントリを含めることはできません


2.5. 戻り値カスタム制約

場合によっては、複雑なオブジェクトも検証する必要があります。

public class ReservationManagement {

    @ValidReservation
    public Reservation getReservationsById(int id) {
        return null;
    }
}

この例では、返される

Reservation

オブジェクトは、次に定義する

@ ValidReservation

で定義された制約を満たす必要があります。

繰り返しますが、まず制約アノテーションを定義する必要があります。

@Constraint(validatedBy = ValidReservationValidator.class)
@Target({ METHOD, CONSTRUCTOR })
@Retention(RUNTIME)
@Documented
public @interface ValidReservation {
    String message() default "End date must be after begin date "
      + "and both must be in the future, room number must be bigger than 0";

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

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

その後、バリデータクラスを定義します。

public class ValidReservationValidator
  implements ConstraintValidator<ValidReservation, Reservation> {

    @Override
    public boolean isValid(
      Reservation reservation, ConstraintValidatorContext context) {

        if (reservation == null) {
            return true;
        }

        if (!(reservation instanceof Reservation)) {
            throw new IllegalArgumentException("Illegal method signature, "
            + "expected parameter of type Reservation.");
        }

        if (reservation.getBegin() == null
          || reservation.getEnd() == null
          || reservation.getCustomer() == null) {
            return false;
        }

        return (reservation.getBegin().isAfter(LocalDate.now())
          && reservation.getBegin().isBefore(reservation.getEnd())
          && reservation.getRoom() > 0);
    }
}


2.6. コンストラクタの戻り値

以前に

ValidReservation

インターフェース内で

METHOD



CONSTRUCTOR



target

として定義したので、** 構築したインスタンスを検証するために

Reservation

のコンストラクタに注釈を付けることもできます。

public class Reservation {

    @ValidReservation
    public Reservation(
      LocalDate begin,
      LocalDate end,
      Customer customer,
      int room) {
        this.begin = begin;
        this.end = end;
        this.customer = customer;
        this.room = room;
    }

   //properties, getters, and setters
}

** 2.7. カスケード検証

最後に、Bean Validation APIを使用すると、いわゆるカスケード検証を使用して、単一のオブジェクトだけでなくオブジェクトグラフも検証できます。

  • したがって、複雑なオブジェクトを検証する場合は、カスケード検証に

    @ Valid

    を使用できます** 。これは、メソッドパラメータと戻り値に対して機能します。

いくつかのプロパティ制約を持つ

Customer

クラスがあるとします。

public class Customer {

    @Size(min = 5, max = 200)
    private String firstName;

    @Size(min = 5, max = 200)
    private String lastName;

   //constructor, getters and setters
}


Reservation

クラスには、

Customer

プロパティと、制約付きの他のプロパティがあります。

public class Reservation {

    @Valid
    private Customer customer;

    @Positive
    private int room;

   //further properties, constructor, getters and setters
}

メソッドパラメータとして

Reservation

を参照すると、すべてのプロパティの再帰的検証を強制することができます。

public void createNewCustomer(@Valid Reservation reservation) {
   //...
}

ご覧のとおり、2つの場所で

@ Valid

を使用しています。


  • reservation

    -parameter:それはの検証を起動します


createNewCustomer()

が呼び出されたときの

Reservation

オブジェクト
** ここにネストしたオブジェクトグラフがあるので、

@ Valid

も追加する必要があります。


customer

-attribute:これにより、このネストしたプロパティの検証がトリガーされます。

これは

Reservation

型のオブジェクトを返すメソッドに対しても機能します。

@Valid
public Reservation getReservationById(int id) {
    return null;
}


3メソッドの制約を検証する

前のセクションで制約を宣言した後、これらの制約を実際に検証することができます。そのためには、複数のアプローチがあります。


3.1. Spring

による自動検証

Spring ValidationはHibernate Validatorとの統合を提供します。

注:Spring ValidationはAOPに基づいており、Spring AOPをデフォルトの実装として使用します。したがって、検証はメソッドに対してのみ機能し、コンストラクタには機能しません。

Springに制約を自動的に検証させたいのであれば、2つのことをする必要があります。

  • 最初に、検証されるBeanに

    @ Validated

    ** という注釈を付けます。

@Validated
public class ReservationManagement {

    public void createReservation(@NotNull @Future LocalDate begin,
      @Min(1) int duration, @NotNull Customer customer){

       //...
    }

    @NotNull
    @Size(min = 1)
    public List<@NotNull Customer> getAllCustomers(){
        return null;
    }
}

次に、

MethodValidationPostProcessor

Beanを提供する必要があります。

@Configuration
@ComponentScan({ "org.baeldung.javaxval.methodvalidation.model" })
public class MethodValidationConfig {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

制約に違反した場合、コンテナは

javax.validation.ConstraintViolationException

をスローするようになりました。

Spring Bootを使用している場合、

hibernate-validator

がクラスパスにある限り、コンテナは

MethodValidationPostProcessor

Beanを登録します。


3.2. CDIによる自動検証(JSR-365)

バージョン1.1以降、Bean ValidationはCDI(Java EEのコンテキストと依存性注入)と連携します。

アプリケーションがJava EEコンテナ内で実行される場合、コンテナは起動時に自動的にメソッド制約を検証します。


3.3. プログラムによる検証

スタンドアロンのJavaアプリケーションでの手動のメソッド検証には、

javax.validation.executable.ExecutableValidator

インタフェースを使用できます。

次のコードを使ってインスタンスを取得できます。

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
ExecutableValidator executableValidator = factory.getValidator().forExecutables();

ExecutableValidatorは4つのメソッドを提供します。


  • validateParameters()

    メソッドおよび

    validateReturnValue()

    メソッド

検証
**

validateConstructorParameters()

および

コンストラクタ検証のための

validateConstructorReturnValue()

最初のメソッド

createReservation()

のパラメータを検証すると、次のようになります。

ReservationManagement object = new ReservationManagement();
Method method = ReservationManagement.class
  .getMethod("createReservation", LocalDate.class, int.class, Customer.class);
Object[]parameterValues = { LocalDate.now(), 0, null };
Set<ConstraintViolation<ReservationManagement>> violations
  = executableValidator.validateParameters(object, method, parameterValues);

  • 注:公式文書では、このコードをアプリケーションコードから直接呼び出すことは推奨していませんが、AOPやプロキシなどのメソッドインターセプトテクノロジを介して使用することを推奨しています。


ExecutableValidator

インターフェースの使用方法に興味がある場合は、http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html__single/#section-validating-exec -ableをご覧ください。制約[公式文書]


4結論

このチュートリアルでは、Hibernate Validatorでメソッド制約を使用する方法について簡単に説明しました。また、JSR-380のいくつかの新機能についても説明しました。

まず、さまざまな種類の制約を宣言する方法について説明しました。

  • 単一パラメータ制約

クロスパラメータ

  • 戻り値の制約

また、Spring Validatorを使用して手動および自動で制約を検証する方法についても説明しました。

いつものように、例の完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/javaxval[GitHubで利用可能]です。