このページに書いてあること
Spring MVCのControllerに @GetMapping を付けると、誰がそのメソッドを呼ぶのでしょうか。GET /greeting と POST /greeting を送り、Springから同じメソッドが呼ばれる条件を比べます。
Frameworkってなんだっけ問題
Base64変換のライブラリを使うときは、自分のコードが必要な箇所で変換メソッドを呼びます。Spring MVCでHTTPを受け付けるときは、リクエストを受け取った側が条件に合うControllerを選びます。違いが分かると、処理が始まらないときにどこを調べるか決められます。
ありがちなFrameworkの理解
ControllerからService、ServiceからRepositoryへ進む矢印は、自分が実装する呼び出しです。この図だけでは、そもそも誰がControllerの入口を呼ぶかが分かりません。

ハリウッドの原則って何だ問題
呼び出し順の一部をフレームワークが持ち、条件がそろった時点でアプリが用意した処理を呼ぶ関係を、ハリウッドの原則と呼びます。Spring MVCでは DispatcherServlet がリクエストを受け、マッピングの条件に合うハンドラへ処理を渡します。@GetMapping("/greeting") は、GETでそのパスに来たときの入口を登録します。DispatcherServletのリクエスト処理

実行用サンプル
Spring MVCのMockMvcで、HTTPメソッドだけを変えた2つのケースを検証します。TomcatなどのサーバーやSpring Bootは起動しません。
JDK 17、Maven 3.9系、Spring Framework 6.2.19。ZIPにはソース全文、pom.xml、READMEを収録しています。展開先で mvn test を実行します。
次のControllerは greet() が呼ばれた回数を、テストから確かめるために記録します。
GreetingController.java の全文です。
package example;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
final class GreetingController {
private int callCount;
@GetMapping("/greeting")
public String greet() {
callCount++;
return "Hello";
}
int callCount() {
return callCount;
}
}MvcCallbackTest.java の全文です。
package example;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
class MvcCallbackTest {
@Test
void getCallsTheControllerAndUsesItsReturnValue() throws Exception {
var controller = new GreetingController();
var mvc = MockMvcBuilders.standaloneSetup(controller).build();
var response = mvc.perform(get("/greeting")).andReturn().getResponse();
assertEquals(200, response.getStatus());
assertEquals("Hello", response.getContentAsString());
assertEquals(1, controller.callCount());
}
@Test
void postDoesNotCallAControllerMappedToGet() throws Exception {
var controller = new GreetingController();
var mvc = MockMvcBuilders.standaloneSetup(controller).build();
var response = mvc.perform(post("/greeting")).andReturn().getResponse();
assertEquals(405, response.getStatus());
assertEquals(0, controller.callCount());
}
}GETでは greet() が1回呼ばれ、HTTP 200と本文 Hello が返ります。POSTでは同じパスでもHTTP 405が返り、呼び出し回数は0です。テストはControllerを登録してHTTPリクエストを渡しており、greet() を直接呼んでいません。
Frameworkで問題が起きた時の勘所
Controllerの先頭にブレークポイントを置き、同じリクエストで止まるかを確認します。止まらず405なら、送ったHTTPメソッドと @GetMapping などの条件を突き合わせます。404なら、実際のリクエストパスやControllerの登録を確認します。メソッドに入ってから失敗したら、その時点の引数と下流の例外を見ます。
Beanを誰が組み立てるかと、処理を誰が呼ぶかは別の観点です。Springは通常のシングルトンBeanをコンテキスト初期化時に組み立てて注入し、Controllerの業務コードはそのServiceを呼び出します。フレームワークがすべてのServiceをリクエストのたびに作るわけではありません。
実際に遭遇した具体例
旧Spring MVCのシステムで、Serviceが必要とするSpring Data JPAのRepositoryが登録されず、起動に失敗したことがあります。Serviceの呼び出し側を変更しても解決せず、Repositoryをどこで登録する構成かを調べました。リクエストを受ける前に止まる場合は、処理本体に入る前の登録や初期化を確認します。
当時のXML構成では、通常のコンポーネントスキャンに加え、jpa:repositories の base-package でRepositoryインターフェースの場所を指定しました。現在のJava設定では @EnableJpaRepositories、Spring Bootでは自動設定で探索される範囲を、採用している構成に合わせて確認します。Spring Data JPAのRepository登録
