1. test 1
  2. test 2
  3. test 3
  4. test 4
  5. test 5
  6. test 6
  7. test 7
コース一覧
Spring Boot入門
パスのマッチング

Spring Boot入門

Java で Web API サーバーを作るための Spring Boot の入門コースです。DI コンテナ、REST コントローラ、リクエスト検証、レイヤ分割、Spring Data JPA、例外ハンドリング、テストまでを扱います。毎レッスン、Spring が裏側でやっている判断そのものを pure Java の関数として自分で書き、フレームワークが何を肩代わりしてくれているのかを手触りで理解できる構成にしました。約 12 時間 (1 日 30 分 × 24 日) で 48 レッスンを修了できます。

1
Spring Bootとは何か
01. Spring Bootで何が作れるのか10分
02. サーブレットからフレームワークへ15分
03. ルーティング表という発想15分
04. プロジェクトの構成10分
05. 起動のしくみと自動設定15分
06. パスのマッチング15分
2
DIコンテナ
01. newで書くと何が困るのか15分
02. コンストラクタインジェクション15分
03. インターフェースへの依存15分
04. @Componentと@Bean15分
05. 自作のDIコンテナ20分
06. Beanのスコープとライフサイクル15分
3
RESTコントローラ
01. RESTという設計方針15分
02. @RestControllerの役割15分
03. @GetMappingとパス変数15分
04. クエリパラメータの受け取り15分
05. @PostMappingとリクエストボディ15分
06. ステータスコードの選び方15分
07. ResponseEntityで組み立てる15分
4
リクエストとレスポンスの変換
01. JSONとオブジェクトの往復15分
02. DTOとエンティティを分ける15分
03. recordでDTOを書く15分
04. 入力検証の基本15分
05. 検証エラーをまとめて返す15分
06. アノテーションによる検証15分
5
レイヤ構成とビジネスロジック
01. 3層アーキテクチャ10分
02. @Serviceに置くもの15分
03. 状態遷移をルールにする15分
04. 料金計算のような業務ロジック15分
05. @Transactionalとは何か15分
06. トランザクションの巻き戻し15分
6
データアクセス
01. @Entityとテーブルの対応15分
02. リポジトリという抽象15分
03. メソッド名からクエリを作る20分
04. 検索条件の組み立て15分
05. ページングと並び替え15分
06. N+1問題15分
7
例外処理と横断的関心事
01. 例外をHTTPに変換する15分
02. @ExceptionHandlerと@ControllerAdvice15分
03. エラーレスポンスの形を決める15分
04. フィルタとインターセプタ15分
05. ログとリクエストの追跡15分
06. 設定値の外部化15分
8
テストと仕上げ
01. テストの種類と使い分け15分
02. Serviceの単体テスト15分
03. モックで依存を差し替える15分
04. MockMvcでエンドポイントを試す15分
05. 本番へ出すための次の一歩10分

パスのマッチング

このレッスンでは、/users/{id} のような URL パターンが実際のパスとどう照合されているのかを掴み、その照合そのものを自分で書きます。

パス変数は、パターンの穴です

REST API では、対象を識別する値を URL の中に埋め込みます。

Java

@RestController public class UserController { @GetMapping("/users/{id}") public User find(@PathVariable Long id) { return userService.findById(id); } @GetMapping("/users/{userId}/posts/{postId}") public Post findPost(@PathVariable Long userId, @PathVariable Long postId) { return postService.find(userId, postId); } }

{id} の部分がパス変数です。これは「ここには何か 1 つの値が入る」という穴で、@PathVariable を付けた引数がその穴の中身を受け取ります。中かっこの中の名前と引数名が一致していれば、対応付けは自動で行われます。名前が違う場合は @PathVariable("id") Long userId のように明示します。

Long と書けば、String から Long への変換も Spring がやってくれます。数字でない値が来た場合は変換に失敗し、コントローラのメソッドは呼ばれずに 400 Bad Request が返ります。

照合は「区切りごとの突き合わせ」です

パターンと実際のパスをどう突き合わせているのか、素朴に考えると次の手順になります。

  1. どちらもスラッシュで区切って断片の列にする
  2. 断片の数が違えば、その時点で不一致
  3. 前から順に 1 つずつ比べる
  4. パターン側の断片が中かっこで囲まれていれば、実際の側の断片は何でもよい。その値を変数として覚えておく
  5. 中かっこでなければ、文字列として完全に一致していなければ不一致
  6. 最後まで比べ終わったら一致

たとえばパターン /users/{id}/posts と実際のパス /users/12/posts を突き合わせると、断片はどちらも 3 つで、1 つめの users は文字列一致、2 つめは {id} なので 12 を覚えて通過、3 つめの posts は文字列一致となり、一致と判定されます。

一方で /users/12 を同じパターンに当てると、断片が 2 つしかないので 2 番目の手順で不一致になります。

パターン実際のパス結果
/users/{id}/users/12一致 id は 12
/users/{id}/users/12/posts不一致 (断片の数が違う)
/users/{id}/orders/12不一致 (users と orders)
/users/{userId}/posts/{postId}/users/3/posts/9一致 userId は 3、postId は 9

Spring が実際に使っている照合はもっと高機能で、* や ** のようなワイルドカード、拡張子の指定、複数のパターンが同時に当たったときにどちらを優先するかという順位付けまで持っています。しかし骨格はここに書いた手順そのものです。

演習でやること

パターンと実際のパスの 2 つの文字列を受け取り、照合して結果を返す関数を書きます。一致した場合は取り出した変数を id=12 のような形でつなぎ、変数が複数あればカンマで区切ります。一致しない場合は no-match を返します。

Spring では @GetMapping("/users/{id}") と @PathVariable を書くだけで済んでいる処理を、ここでは自分で書きます。書き終えたあとにもう一度アノテーションを見ると、あの 2 行がどれだけの仕事を隠しているのかが分かるはずです。

要件

  1. パターンと実際のパスをスラッシュで区切って断片に分け、空の断片は無視すること。断片の数が違えば "no-match" を返すこと
  2. パターン側の断片が中かっこで囲まれていればパス変数として値を取り出し、そうでなければ完全一致するか確かめること
  3. 取り出したパス変数を name=value の形でカンマ区切りにして返し、不一致なら "no-match" を返すこと
  4. パターンに変数が1つも無く、それでも一致した場合は match を返すこと

入出力例

matchPath("/users/{id}", "/users/12") → "id=12" matchPath("/users/{id}/posts", "/users/12/posts") → "id=12" matchPath("/users/{userId}/posts/{postId}", "/users/3/posts/9") → "userId=3,postId=9" matchPath("/health", "/health") → "match" matchPath("/users/{id}", "/users/12/posts") → "no-match" matchPath("/users/{id}", "/orders/12") → "no-match" matchPath("/users/{id}/posts", "/users/12/comments") → "no-match"

ヒント

split("/") をすると先頭のスラッシュのせいで空文字列の要素ができます。空の断片を捨ててから比較すると数の判定が楽になります

中かっこかどうかは startsWith("{") と endsWith("}") で判定し、substring(1, 長さ - 1) で変数名を取り出します

結果は StringBuilder に積み、2件目以降を足す前にカンマを付けると余計なカンマが残りません

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/09

関連レッスン

  • newで書くと何が困るのか

    直接 new する設計の差し替えにくさを、コードで確かめます。

  • RESTという設計方針

    リソースと操作から URL とメソッドを導けるようになります。

  • JSONとオブジェクトの往復

    キーと値の対応づけを自分で実装し、変換の中身を掴みます。

  • 3層アーキテクチャ

    層を分ける理由と、それぞれの責務を説明できるようになります。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • URLページの住所全体
  • 変数データに名前をつけて参照する仕組み
  • REST APIリソースを URL で表現する HTTP API 設計
  • エンドポイント「この URL を叩くとこの機能が動く」入口の住所
  • APIアプリ間でデータをやり取りする窓口
  • 引数位置引数=順番で渡す。
  • メソッドクラスに属する関数
  • 関数処理に名前を付けて再利用できる単位
main.java
学習モード
エディタを読み込んでいます

メモ

パスのマッチング

⌘S で保存