[Learn] Spring Boot 入門:用 BootController 建立第一個 API
/ 7 min read
Table of Contents
前面的 Java 文章整理了 class、interface 與 constructor,接下來把這些概念放進一個可以接收 HTTP request 的程式
Spring Boot 系列分成三篇,依序閱讀即可在同一個專案上繼續修改
- 第一篇:用
BootController建立 API,理解 request 如何進入 Java method - 第二篇:用 Service 與 Repository 拆開業務規則和資料存取
- 第三篇:理解 DI、Bean 與 Spring 如何建立這些物件
這篇的 BootController 是自己取的 class 名稱,不是 Spring 提供的特殊 class,也沒有 @BootController annotation
Spring 與 Spring Boot 各自做什麼
Spring Framework 提供 DI container、Spring MVC、transaction 等能力
Spring Boot 建立在 Spring 之上,提供 starter dependency、auto-configuration 與應用程式啟動方式,減少開始寫功能之前需要處理的設定
以 HTTP API 為例,選入 Spring Web 後,Boot 會依照 dependency 與設定配置 web application 所需的元件,通常也會啟動 embedded server
不是每一個 Spring Boot 專案都一定是 web server,這取決於加入的 dependency 與應用程式設定
建立範例專案
開啟 Spring Initializr,使用以下設定
| 欄位 | 值 |
|---|---|
| Project | Maven |
| Language | Java |
| Spring Boot | 選穩定版,不選 SNAPSHOT 或 milestone |
| Group | com.example |
| Artifact | bootdemo |
| Package name | com.example.bootdemo |
| Packaging | Jar |
| Java | 21 |
| Dependencies | Spring Web |
本系列使用 Java 21,範例採用 Spring Boot 3.x/4.x 共通的 Spring MVC 與 DI API,dependency 版本交由 Initializr 產生的專案管理
解壓縮後用 IDE 開啟專案,保留產生的 pom.xml 與 Maven Wrapper,不需要自己下載 Spring JAR
確認 terminal 與 IDE 使用相同的 JDK
java -version./mvnw -versionWindows 可以把本文的 ./mvnw 改成 mvnw.cmd
這篇會用到以下檔案
bootdemo/├── pom.xml├── mvnw└── src/main/java/com/example/bootdemo/ ├── BootdemoApplication.java ├── BootController.java └── BootMessage.javaApplication 是啟動入口
Initializr 會產生 BootdemoApplication.java
package com.example.bootdemo;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class BootdemoApplication { public static void main(String[] args) { SpringApplication.run(BootdemoApplication.class, args); }}main 仍然是 Java 的程式入口,SpringApplication.run(...) 則負責啟動 Spring application
@SpringBootApplication 組合了三個主要能力
@SpringBootConfiguration:讓這個 class 成為 configuration,底層也是@Configuration@EnableAutoConfiguration:根據 dependency、設定與既有 Bean 決定套用哪些自動設定@ComponentScan:掃描元件,預設從這個 class 所在的 package 往下找
因此把入口放在 com.example.bootdemo,其他元件放在相同 package 或 subpackage,是容易理解的起點
如果 Controller 放在 com.other,預設掃描不會找到它,即使有加 annotation 也不會自動出現 API
這些能力的組合可對照 Spring Boot 官方說明
BootController 接收 HTTP request
建立 BootController.java
package com.example.bootdemo;
import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;
@RestController@RequestMapping("/api/boot")public class BootController { @GetMapping("/hello") public BootMessage hello( @RequestParam(name = "name", defaultValue = "visitor") String name) { return new BootMessage("Hello, " + name); }}再建立 BootMessage.java
package com.example.bootdemo;
public record BootMessage(String message) {}record 適合用來表達簡單資料,這裡會有 constructor、message() accessor,以及產生的 equals、hashCode、toString
這個 record 只有一個 String component,拿來當 response DTO 很直接,DTO 是用來傳遞資料的物件
幾個 annotation 各自負責不同的事情
| annotation | 作用 |
|---|---|
@RestController | 標記 web controller,method 回傳值預設寫入 response body |
@RequestMapping("/api/boot") | 設定這個 class 的共同路徑 |
@GetMapping("/hello") | 把 GET request 對應到 method |
@RequestParam | 把 query parameter 綁定到 Java parameter |
class 與 method 上的路徑合起來,就是 GET /api/boot/hello
@RequestParam 明確寫出 name,可以直接看出 URL 參數的名稱,也不需要靠 compiler 保留 Java parameter name 才能推導
啟動並觀察 response
在包含 pom.xml 的目錄執行
./mvnw spring-boot:run保持這個 terminal 執行,再開另一個 terminal 發送 request
curl -i 'http://localhost:8080/api/boot/hello?name=Amy'預期 HTTP status 是 200,response body 如下
{ "message": "Hello, Amy" }沒有提供 name 時會套用預設值
curl 'http://localhost:8080/api/boot/hello'{ "message": "Hello, visitor" }Java method 回傳的是 BootMessage object,Spring MVC 透過 HTTP message converter 把它轉成 JSON,不需要自己組 JSON 字串
這裡依賴 Spring Web 專案配置的 JSON 支援,@RestController 本身不代表任何型別都一定輸出 JSON,例如回傳 String 通常會得到文字內容
Controller 與 JSON response 的基本行為可對照 Spring 官方 REST guide
一次 request 經過哪些地方
curl / browser ↓ HTTP GET /api/boot/hello?name=Amyembedded web server ↓Spring MVC DispatcherServlet ↓ 根據 mapping 找到 handler,綁定 parameterBootController.hello("Amy") ↓ 回傳 BootMessageHTTP message converter ↓HTTP 200 + JSON response bodyController 的 method 不需要自己讀 socket,也不需要自己解析完整 HTTP request
接下來加入資料查詢時,可以讓 Controller 專心處理 HTTP,再把工作交給其他 Java object
常見混淆
@Controller 和 @RestController 不完全相同
一般 @Controller 的 method 若回傳 String,通常會被當成 view name;要直接寫入 response body,可以另外加 @ResponseBody
@RestController 組合了 @Controller 與 @ResponseBody,很適合這種回傳資料的 API
@GetMapping 也不是「讓 method 自己開始執行」的 annotation,必須有符合路徑與 HTTP method 的 request 才會進入對應處理流程
如果得到 404,先檢查完整 URL、mapping 與 Controller 的 package;如果啟動時顯示 port 已被使用,可以先在 src/main/resources/application.properties 加上以下設定
server.port=8081改完重啟程式,curl 的 port 也要一起改成 8081
接下來拆出 Service 與 Repository
現在 Controller 直接組合訊息,功能很小時容易閱讀
當需求變成「查詢學生,再判斷是否及格」,就會同時碰到 HTTP、業務規則與資料來源