Springのインターフェース駆動型コントローラー
1. 序章
このチュートリアルでは、通常のJavaインターフェイスを使用してWebリクエストを指定できるSpringMVCの新機能について検討します。
2. 概要
通常、Spring MVCでコントローラーを定義するときは、リクエストを指定するさまざまなアノテーション(エンドポイントのURL、HTTPリクエストメソッド、パス変数など)でそのメソッドを装飾します。
たとえば、 / save / {id} エンドポイントを、他の方法では単純な方法で前述のアノテーションを使用して導入できます。
@PostMapping("/save/{id}")
@ResponseBody
public Book save(@RequestBody Book book, @PathVariable int id) {
// implementation
}
当然、リクエストを処理するコントローラーが1つしかない場合、これはまったく問題になりません。 同じメソッドシグネチャを持つさまざまなコントローラーがある場合、状況は少し変わります。
たとえば、移行などのために、同じメソッドシグネチャを持つ2つの異なるバージョンのコントローラがある場合があります。 その場合、メソッド定義に付随するかなりの量の重複したアノテーションがあります。 明らかに、それはDRY(自分自身を繰り返さない)の原則に違反します。
この状況が純粋なJavaクラスで発生する場合は、インターフェイスを定義し、クラスにこのインターフェイスを実装させるだけです。 コントローラでは、メソッドの主な負担は、メソッドのシグネチャではなく、メソッドのアノテーションによるものです。
ただし、Spring 5.1では新しい機能が導入されました:
コントローラーパラメーターの注釈は、インターフェースでも検出されます。コントローラーインターフェースで完全なマッピングコントラクトを許可します。
この機能をどのように使用できるかを調べてみましょう。
3. コントローラーのインターフェース
3.1. コンテキスト設定
書籍を管理する非常に単純なRESTアプリケーションの例を使用して、新機能を説明します。 これは、本を取得および変更できるメソッドを備えた1つのコントローラーのみで構成されます。
チュートリアルでは、機能に関連する問題のみに焦点を当てます。 アプリケーションのすべての実装の問題は、GitHubリポジトリにあります。
3.2. インターフェース
メソッドのシグネチャだけでなく、メソッドが処理することになっているWebリクエストのタイプも定義する通常のJavaインターフェイスを定義しましょう。
@RequestMapping("/default")
public interface BookOperations {
@GetMapping("/")
List<Book> getAll();
@GetMapping("/{id}")
Optional<Book> getById(@PathVariable int id);
@PostMapping("/save/{id}")
public void save(@RequestBody Book book, @PathVariable int id);
}
メソッドレベルのアノテーションだけでなく、クラスレベルのアノテーションもあることに注意してください。 これで、このインターフェイスを実装するコントローラーを作成できます。
@RestController
@RequestMapping("/book")
public class BookController implements BookOperations {
@Override
public List<Book> getAll() {...}
@Override
public Optional<Book> getById(int id) {...}
@Override
public void save(Book book, int id) {...}
}
クラスレベルのアノテーション@RestControllerまたは@Controllerをコントローラーに追加する必要があります。このように定義すると、コントローラーはWebリクエストのマッピングに関連するすべてのアノテーションを継承します。
コントローラが期待どおりに動作することを確認するために、アプリケーションを実行し、対応するリクエストを行って getAll()メソッドを実行します。
curl http://localhost:8081/book/
コントローラがインターフェイスを実装している場合でも、Webリクエストの注釈を追加することでさらに微調整できます。 クラスレベルまたはメソッドレベルのいずれかで、インターフェイスに対して行ったのと同じ方法でこれを行うことができます。 実際、コントローラーを定義するときにこの可能性を使用しました。
@RequestMapping("/book")
public class BookController implements BookOperations {...}
コントローラにWebリクエストのアノテーションを追加すると、それらはインターフェイスのアノテーションよりも優先されます。 言い換えると、 Springは、Javaが継承を処理する方法と同様の方法でコントローラーインターフェイスを解釈します。
インターフェースですべての一般的なWeb要求プロパティを定義しますが、コントローラーでは常にそれらを微調整できます。
3.3. 注意注意
インターフェイスとそれを実装するさまざまなコントローラーがある場合、Web要求が複数のメソッドで処理される可能性があります。 当然、Springは例外をスローします。
Caused by: java.lang.IllegalStateException: Ambiguous mapping.
コントローラを@RequestMappingで装飾すると、マッピングがあいまいになるリスクを減らすことができます。
4. 結論
このチュートリアルでは、Spring5.1で導入された新機能について検討しました。 現在、Spring MVCコントローラーがインターフェースを実装する場合、標準のJavaの方法でこれを行うだけでなく、インターフェースで定義されたすべてのWeb要求関連機能も継承します。
いつものように、対応するコードスニペットはGitHubリポジトリにあります。