1. 概要

このチュートリアルでは、Javaアプリケーションのサービス層でのSpring検証について説明します。 Spring Bootはカスタムバリデーターとのシームレスな統合をサポートしていますが、検証を実行するためのデファクトスタンダードはHibernateValidatorです。

ここでは、検証ロジックをコントローラーから別のサービスレイヤーに移動する方法を学習します。 さらに、Springアプリケーションのサービスレイヤーに検証を実装します。

2. アプリケーションの階層化

要件に応じて、Javaビジネスアプリケーションはいくつかの異なる形とタイプをとることができます。 たとえば、これらの基準に基づいて、アプリケーションに必要なレイヤーを決定する必要があります。 特定のニーズがない限り、多くのアプリケーションは、サービス層またはリポジトリ層の複雑さと保守コストの増加による恩恵を受けません。

複数のレイヤーを使用することで、これらすべての懸念を満たすことができます。 これらのレイヤーは次のとおりです。

コンシューマーレイヤーまたはWebレイヤーは、Webアプリケーションの最上位レイヤーです。 ユーザーの入力を解釈し、適切な応答を提供することを担当します。 他のレイヤーによってスローされた例外も、Webレイヤーによって処理される必要があります。 Webレイヤーはアプリケーションのエントリポイントであるため、認証を担当し、許可されていないユーザーに対する保護の最前線として機能します。

Webレイヤーの下にはサービスレイヤーがあります。 これはトランザクションの障壁として機能し、アプリケーションサービスとインフラストラクチャサービスの両方を収容します。 さらに、サービス層のパブリックAPIは、アプリケーションサービスによって提供されます。 多くの場合、トランザクションの境界として機能し、トランザクションの承認を担当します。 インフラストラクチャサービスは、ファイルシステム、データベース、電子メールサーバーなどの外部ツールに接続する「配管コード」を提供します。 これらのアプローチは、多くの場合、いくつかのアプリケーションサービスで使用されます。

Webアプリケーションの最下層は永続層です。つまり、ユーザーのデータストレージとの対話を担当します。

3. サービスレイヤーでの検証

サービスレイヤーは、コントローラーと永続レイヤーの間の通信を容易にするアプリケーション内のレイヤーです。 さらに、ビジネスロジックはサービスレイヤーに格納されます。 特に検証ロジックが含まれています。 モデルの状態は、コントローラーとサービスレイヤー間の通信に使用されます。

検証をビジネスロジックとして扱うことには長所と短所があり、Springの検証(およびデータバインディング)アーキテクチャもそれを排除するものではありません。 特に、検証はWeb層にバインドされるべきではなく、ローカライズが簡単であり、利用可能なバリデーターの使用を許可する必要があります。

また、クライアント入力データは常にRESTコントローラープロセスを通過するとは限りません。サービスレイヤーでも検証しないと、受け入れられないデータが通過し、いくつかの問題が発生する可能性があります。 この場合、標準のJavaJSR-303検証スキームを使用します。

4.

Spring Bootを使用して開発された簡単なユーザーアカウント登録フォームを考えてみましょう。

4.1. 単純なドメインクラス

まず、名前、年齢、電話番号、パスワードの属性のみを使用します。

public class UserAccount {

    @NotNull(message = "Password must be between 4 to 15 characters")
    @Size(min = 4, max = 15)
    private String password;

    @NotBlank(message = "Name must not be blank")
    private String name;

    @Min(value = 18, message = "Age should not be less than 18")
    private int age;

    @NotBlank(message = "Phone must not be blank")
    private String phone;
    
    // standard constructors / setters / getters / toString
}

上記のクラスでは、 @NotNull @Size @NotBlank 、および@Minの4つのアノテーションを使用しました。入力属性がnullでも空白でもないこと、およびサイズ要件に準拠していることを確認します。

4.2. サービスレイヤーでの検証の実装

利用可能な検証ソリューションは多数あり、SpringまたはHibernateが実際の検証を処理します。 一方、手動検証は実行可能な代替手段です。 検証をアプリの適切な部分に統合することになると、これにより多くの柔軟性が得られます。

次に、サービスクラスに検証を実装しましょう。

@Service
public class UserAccountService {

    @Autowired
    private Validator validator;
    
    @Autowired
    private UserAccountDao dao;
    
    public String addUserAccount(UserAccount useraccount) {
        
        Set<ConstraintViolation<UserAccount>> violations = validator.validate(useraccount);

        if (!violations.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (ConstraintViolation<UserAccount> constraintViolation : violations) {
                sb.append(constraintViolation.getMessage());
            }
            throw new ConstraintViolationException("Error occurred: " + sb.toString(), violations);
        }

        dao.addUserAccount(useraccount);       
        return "Account for " + useraccount.getName() + " Added!";
    }
}

ValidatorはBeanValidationAPIの一部であり、Javaオブジェクトの検証を担当します。 さらに、Springは Validator インスタンスを自動的に提供します。これは、UserAccountServiceに挿入できます。 Validator は、 validate(..)関数内で渡されたオブジェクトを検証するために使用されます。 結果は、ConstraintViolationSetです。

検証制約に違反していない場合(オブジェクトが有効である場合)、Setは空です。 それ以外の場合は、ConstraintViolationExceptionをスローします。

4.3. RESTコントローラーの実装

この後、Spring RESTコントローラークラスを構築して、クライアントまたはエンドユーザーにサービスを表示し、アプリケーションの入力検証を評価しましょう。

@RestController
public class UserAccountController {

    @Autowired
    private UserAccountService service;

    @PostMapping("/addUserAccount")
    public Object addUserAccount(@RequestBody UserAccount userAccount) {
        return service.addUserAccount(userAccount);
    }
}

上記のRESTコントローラーフォームでは、検証を防ぐために@Validアノテーションを使用していません。

4.4. RESTコントローラーのテスト

それでは、Spring Bootアプリケーションを実行してこのメソッドをテストしてみましょう。 その後、Postmanまたはその他のAPIテストツールを使用して、JSON入力を localhost:8080 / addUserAccountURLに投稿します。

{
   "name":"Baeldung",
   "age":25,
   "phone":"1234567890",
   "password":"test",
   "useraddress":{
      "countryCode":"UK"
   }
}
テストが正常に実行されたことを確認したら、検証が期待どおりに機能しているかどうかを確認しましょう。 次の論理的な手順は、無効な入力をほとんど使用せずにアプリケーションをテストすることです。 したがって、入力JSONを無効な値で更新します。
{
   "name":"",
   "age":25,
   "phone":"1234567890",
   "password":"",
   "useraddress":{
      "countryCode":"UK"
   }
}
コンソールにエラーメッセージが表示されるようになりました。したがって、 Validatorの使用が検証に不可欠であることがわかります
Error occurred: Password must be between 4 to 15 characters, Name must not be blank

5. 長所と短所

サービス/ビジネスレイヤーでは、これは検証に成功するアプローチであることがよくあります。メソッドパラメーターに制限されず、さまざまなオブジェクトに適用できます。 たとえば、データベースからオブジェクトをロードし、それを変更してから、先に進む前に検証する場合があります。

このメソッドを単体テストに使用して、実際にServiceクラスをモックすることもできます。 単体テストでの実際の検証を容易にするために、必要なValidatorインスタンスを手動で生成できます。

どちらの場合も、テストでSpringアプリケーションコンテキストをブートストラップする必要はありません。

6. 結論

このクイックチュートリアルでは、Javaビジネスアプリケーションのさまざまなレイヤーについて説明しました。 検証ロジックをコントローラーから別のサービスレイヤーに移動する方法を学びました。 さらに、Springアプリケーションのサービスレイヤーで検証を実行するための1つのアプローチを実装しました。

例のコードは、GitHubから入手できます。