一、簡介
SpringMVC 就是 Spring 框架中的 MVC 模塊,用于構建 Web 應用中的“控制層”。
SpringMVC 是 Spring 提供的一個基于 Servlet 的 Web MVC 框架模塊,是 Spring 整個體系中的“Web 層核心”。
SpringMVC 是 Spring 的一部分,Spring 框架的主要模塊包括:
1. Core(核心容器)
2. AOP(面向切面)
3. Data Access(JDBC、ORM)
4. Web(包括 Web、Web MVC、Web WebSocket)
5. Messaging
6. Test
1-1、SpringMVC 的職責是什么?
它實現了經典的 MVC 架構中的 控制器 Controller 和前端分發器 DispatcherServlet 功能。
MVC分工:
角色 | SpringMVC 中的實現 |
---|---|
Model | Service 層 + Java Bean |
View | JSP、Thymeleaf、Freemarker 等 |
Controller | 控制層,工程中的servlet,@Controller / @RestController 注解的類;功能:接受請求,響應瀏覽器 |
Dispatcher | DispatcherServlet ,是 SpringMVC 的核心 |
JavaBean分為兩類:
一類稱為實體類Bean:專門存儲業務數據的,如 Student、User 等
一類稱為業務處理 Bean:指 Service 或 Dao 對象,專門用于處理業務邏輯和數據訪問。?
1-2、MVC的工作流程:
┌───────────────┐
瀏覽器請求 ---> │ DispatcherServlet │└───────────────┘│▼┌────────────────────┐│ Controller(控制器) │ <== 負責接收請求、調服務└────────────────────┘│▼┌────────────────────┐│ Service(業務邏輯) │ <== 處理邏輯,調數據庫└────────────────────┘│▼┌────────────────────┐│ DAO / Model(數據) │ <== 數據層/模型層└────────────────────┘│▼┌────────────────────┐│ View(JSP/HTML) │ <== 渲染頁面└────────────────────┘
1-3、SpringMVC 和 SpringBoot 是什么關系?
-
SpringMVC 是“Spring體系中的 MVC 模塊”
-
SpringBoot 是“簡化 Spring 配置的一套框架”
-
SpringBoot 內部集成了 SpringMVC,所以你寫 SpringBoot Web 項目,底層其實就是用的 SpringMVC。
1-4、SpringMVC 和 Servlet 是什么關系?
SpringMVC 是基于 Servlet 的高級封裝:
對比 | Servlet | SpringMVC |
---|---|---|
入口 | 每個 Servlet 寫一個類 | 一個 DispatcherServlet 就能接收所有請求 |
映射 | 在 web.xml 或注解中配置路徑 | 使用 @RequestMapping 等注解 |
請求處理 | 自己解析參數 | 自動綁定參數(甚至對象) |
響應處理 | 手動寫輸出 | 自動 JSON 返回或視圖渲染 |
擴展性 | 不好擴展 | 支持攔截器、參數解析器、數據轉換器 |
二、SpringMVC的特點
2-1、SpringMVC 的核心特點(總結 + 示例 + 對比)
1. 基于注解,開發簡潔
使用注解(如
@Controller
,@RequestMapping
,@ResponseBody
)快速定義控制器和請求路徑。
示例:
@Controller
public class UserController {@RequestMapping("/hello")@ResponseBodypublic String sayHello() {return "Hello SpringMVC!";}
}
對比傳統 Servlet:
傳統 Servlet | SpringMVC 注解風格 |
---|---|
需配置 web.xml 映射路徑 | 用注解快速映射 URL |
手動解析參數 | 自動參數綁定 |
寫出響應內容麻煩 | 支持 @ResponseBody 返回 JSON |
2. 請求參數自動綁定
SpringMVC 自動將請求參數綁定到方法參數、對象屬性,支持類型轉換。
示例:
@PostMapping("/addUser")
public String add(User user) {// 請求中 name=Tom&age=18 自動注入到 user 對象return "ok";
}
對比傳統 Servlet:
Servlet 中這樣寫 |
---|
String name = request.getParameter("name"); |
手動封裝 User 實例 |
3. 強大的 RESTful 支持
SpringMVC 原生支持 REST 風格請求(不同方法映射到不同邏輯)。
示例:
@GetMapping("/user/{id}")
public User getUser(@PathVariable int id) { ... }@DeleteMapping("/user/{id}")
public String deleteUser(@PathVariable int id) { ... }
對比傳統方式:
Servlet 只能識別 URL,不能區分 GET/POST/DELETE/PATCH,需手動判斷 request.getMethod()
4. 內置視圖解析器支持多種視圖(JSP/Thymeleaf/JSON)
說明:
SpringMVC 可返回邏輯視圖名,由視圖解析器處理,或返回 JSON 數據(配合 @ResponseBody
或 @RestController
)
示例:
@GetMapping("/list")
public String list(Model model) {model.addAttribute("students", studentService.getAll());return "studentList"; // JSP 或 Thymeleaf
}
或者:
@RestController
@RequestMapping("/api")
public class ApiController {@GetMapping("/info")public Map<String, Object> info() {return Map.of("status", "ok", "time", System.currentTimeMillis());}
}
5. 全局異常處理、攔截器、數據轉換器可插拔擴展
你可以很方便地實現:
-
自定義參數校驗器(
@Valid
+@ControllerAdvice
) -
請求攔截器(
HandlerInterceptor
) -
統一異常處理(
@ExceptionHandler
/@ControllerAdvice
)
2-2、總結對比表:SpringMVC vs Servlet vs Struts2
比較維度 | SpringMVC | Servlet | Struts2 |
---|---|---|---|
架構模式 | MVC | 非 MVC,結構混亂 | MVC |
開發方式 | 注解驅動,自動裝配 | 手動獲取 request/response | 配置繁瑣,OGNL 綁定 |
REST 支持 | ? 原生支持 | ? 不支持 | ? 需要插件支持 |
參數綁定 | ? 自動對象綁定 + 轉換器 | ? 手動處理 | ? OGNL,但性能較差 |
異常處理 | ? 注解 + 全局統一 | ? 需 try-catch | ? 自定義 filter 實現 |
視圖選擇 | 多視圖支持 | JSP | JSP / Freemarker |
JSON 返回 | ? 內置支持 | ? 需寫輸出流 | ? 支持 JSON 插件 |
社區活躍度 | ? 非常高,主流標準 | ? 已過時 | ? 棄用趨勢 |
2-3、RESTful 接口
RESTful 接口開發 = “資源 URL” + “HTTP 方法”,在 SpringMVC 中,就是用 @GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
來準確表達“我要對這個資源做什么”。
舉個比喻:
-
/users
是一群“用戶” -
/users/1
是用戶 1 -
然后你要“干什么”,就用 HTTP 方法說清楚
想干什么 | URL | 方法 | 注解 |
---|---|---|---|
查所有用戶 | /users | GET | @GetMapping |
新增一個用戶 | /users | POST | @PostMapping |
查詢某個用戶 | /users/1 | GET | @GetMapping("/{id}") |
修改某個用戶 | /users/1 | PUT | @PutMapping("/{id}") |
刪除某個用戶 | /users/1 | DELETE |
|
三、idea創建spring-mvc項目 (maven)
1、不使用maven-webapp模版,直接創建maven項目
2、在pom.xml中導入相關依賴
<dependencies><!-- spring MVC --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.1</version></dependency><!-- 日志 --><!-- 日志門面 API --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.30</version></dependency><!-- slf4j 到 logback 的實現橋接器(含 core 和 classic)--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><!-- servlet api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><!-- tomcat中自帶了servlet api和jsp的jar包的! --><!-- provided:已被提供,當項目打成war包,這個servlet的jar包就不會存在于當前war包中的web-inf的lib中 --><scope>provided</scope></dependency><!-- spring5和thymeleaf的整合包 --><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId><version>3.0.11.RELEASE</version></dependency></dependencies>
【注意】:
????????pom.xml中添加的依賴對應的jar,在當前project打成war包后,會被自動加入到web-inf下的lib文件夾下,但是加了<scope>provided<scope>的,不會被加入到web-inf下的lib文件夾下。
3、添加webapp文件夾
4、添加web.xml文件
【注意】:
修改此處的路徑地址!
3-1、手動配置 DispatcherServlet
—— SpringMVC 的核心組件(前端控制器)
DispatcherServlet 是整個 SpringMVC 的入口
配置 DispatcherServlet 的兩種方式(推薦 XML)?
1、默認配置方式
springMVC的配置文件默認位于web-inf下:
<!-- 注冊 DispatcherServlet,對瀏覽器發送的請求統一處理 --><servlet><!--這個 <servlet-name> 其實是給 DispatcherServlet 起的一個“別名”自定義它與配置文件名有關!當你不給 DispatcherServlet 指定配置文件路徑時(省略 <init-param>),Spring 會默認去加載這個路徑:/WEB-INF/<servlet-name>-servlet.xml示例:如果你寫成:<servlet-name>abc</servlet-name>默認加載:/WEB-INF/abc-servlet.xml但是可以通過 <init-param> 指定配置文件:--><servlet-name>springMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class></servlet><servlet-mapping><servlet-name>springMVC</servlet-name><!--給 Servlet(如 DispatcherServlet)指定它要攔截哪些 URL 請求設置 springMVC 的核心控制器所能處理的請求的請求路徑/ 所匹配的請求可以是 /login 或.html 或.js 或.css 方式的請求路徑但是 / 不能匹配.jsp 請求路徑的請求/*,匹配所有的請求路徑,包括.jsp 請求路徑的請求--><url-pattern>/</url-pattern></servlet-mapping>
但是maven項目,約定,所有的配置文件要統一放到resource文件夾下!
所以,推薦用擴展配置方式?
<url-pattern>/</url-pattern>:將所有的請求都交給 DispatcherServlet 來處理(也就是 SpringMVC)
<url-pattern>
有哪些寫法??
寫法 | 說明 |
---|---|
/ | 攔截所有請求(包括 .jsp 和靜態資源,注意需特殊配置) |
*.do | 只攔截 .do 結尾的請求 |
/app/* | 攔截以 /app/ 開頭的路徑 |
/hello | 只攔截 /hello 這個路徑 |
*.action | 攔截 .action 的請求(老項目常見) |
2、擴展配置方式 (推薦)
<!-- 注冊 DispatcherServlet,對瀏覽器發送的請求統一處理 --><servlet><servlet-name>springMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 加載 SpringMVC 配置文件:/resource/springMVC.xml --><init-param><param-name>contextConfigLocation</param-name><!-- classpath:默認就是當前項目下的resources文件夾下--><param-value>classpath:springMVC.xml</param-value></init-param><!--指定當前 Servlet 是否在服務器啟動時立即加載(初始化),以及加載的優先級。--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springMVC</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
<load-on-startup>
是干什么用的?指定當前 DispatcherServlet 是否在服務器啟動時立即加載(初始化),以及加載的優先級。?
值 含義 大于等于 0 啟動Tomcat時立即創建 Servlet 實例,值越小優先級越高(先加載) 小于 0 或不寫 服務器啟動時不會加載這個 Servlet,第一次請求它時才創建實例(要創建的內容很懂的時候,會影響性能!)
若是:第一次請求時才會加載 DispatcherServlet(延遲初始化),可能出現“首次訪問慢”或配置未生效問題
創建springMVC.xml配置文件?
3-2、創建SpringMVC 中的“控制器”:Controller 類
被 @Controller
注解的類,就是 SpringMVC 中用于接收請求、處理業務邏輯、返回視圖或數據的控制器。
示例:
@Controller
public class HelloController {// "/" -->/WEB-INF/views/index.html@RequestMapping("/")public String index(){return "index";}}
3-3、配置springMVC.xml配置文件
既然使用了注解,就要配置掃描組件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 開啟注解驅動 --><mvc:annotation-driven/><!-- 掃描 Controller 包 --><context:component-scan base-package="com.wsbazinga.controller"/><!-- 視圖解析器 --><bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"><!-- 可以有多個視圖解析器,用order屬性配置優先級 --><property name="order" value="1"/><property name="characterEncoding" value="UTF-8"/><property name="templateEngine"><bean class="org.thymeleaf.spring5.SpringTemplateEngine"><property name="templateResolver"><bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"><!-- 視圖前綴 --><property name="prefix" value="/WEB-INF/views/"/><!-- 視圖后綴 --><property name="suffix" value=".html"/><property name="characterEncoding" value="UTF-8"/><property name="templateMode" value="HTML"/></bean></property></bean></property></bean></beans>
1、<mvc:annotation-driven/>說明:?
?啟用 SpringMVC 對注解(如 @RequestMapping
、@ResponseBody
、@RequestParam
等)的支持功能,也就是告訴 Spring:“我要用注解方式來開發控制器”。
它自動注冊以下組件:
組件名 | 作用 |
---|---|
RequestMappingHandlerMapping | 處理 URL 和方法的映射關系 |
RequestMappingHandlerAdapter | 調用控制器方法并處理參數和返回值 |
HttpMessageConverter | 負責 JSON ? Java 對象 的轉換(如使用 Jackson) |
Validator | 表單參數校驗支持(如 @Valid ) |
3-4、配置tomcat啟動項目?
?
此時,直接啟動tomcat,會通過域名:http://localhost:8080/WsSpringMvc/,直接訪問web-inf/views/index.html文件!
3-5、跳轉到制定頁面
inde.html
<body><h1>首頁</h1><!-- 以/開頭的路徑:絕對路徑(瀏覽器解析、服務器解析) --><!-- thymeleaf語法檢測到是絕對路徑,自動添加上下文路徑,不怕部署路徑變化。 --><a th:href="@{/target}">訪問目標頁面target.html</a></body>
HelloController.java
@RequestMapping("/target")public String toTarget(){return "target";}
再添加target.html即可。
四、@RequestMapping注解講解
@RequestMapping
是用來 映射瀏覽器請求 URL 到后端控制器Controller類?的注解,是 SpringMVC 中的“路由”。
4-1、基本用法
@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/hello")public String hello() {return "hello";}
}
當用戶訪問:http://localhost:8080/項目名/hello
就會調用這個方法,然后返回 "hello"
,通過視圖解析器跳轉到頁面。
【注意】:
1、@RequestMapping注解可以放在類上,也可以放在方法上!
?2、確保
@RequestMapping映射的URL在當前的project中是唯一的!
4-2、常用屬性詳解:
屬性名 | 作用 |
---|---|
value 或 path | 請求的 URL 路徑(必寫),類型是String[]數組 |
method | 限定請求方式(GET、POST 等),類型是:RequestMethod[]數組,若是不設置method屬性,任何的請求方式都能匹配! |
params | 請求必須包含某些參數時才執行,類型:String[] 數組,多個params必須同時滿足 |
headers | 請求頭匹配時才執行 |
produces | 指定響應的內容類型(如 application/json ) |
consumes | 指定請求的數據類型(如 application/json ) |
1. 指定 GET 請求
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getUser() {return "user";
}
訪問 /user
且必須是 GET 請求,否則報錯。
2. 指定多個方法
@RequestMapping(value = "/user", method = {RequestMethod.GET, RequestMethod.POST})
支持 GET 和 POST。
3. 限定參數存在
@RequestMapping(value = "/search", params = "keyword")
只有請求中帶有 keyword
參數才會匹配這個方法。
示例:
<form th:action="@{/testParams(password=1234)}" method="get"><input type="submit" value="測試requestmapping注解的params參數"></form>
@RequestMapping(value = "/testParams",params = {"!username", "password!=123456"})public String testParams(){return "success";}
4. 映射類和方法組合路徑(推薦做法)
@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/add")public String addUser() {return "add";}@RequestMapping("/delete")public String deleteUser() {return "delete";}
}
最終路徑就是 /user/add
、/user/delete
5、請求頭屬性
6、SpringMVC支持ant風格的路徑?
Ant 風格路徑是 SpringMVC 中的一種通配符路徑匹配規則,主要用于 @RequestMapping
的 value
屬性中,用來更靈活地匹配 URL。
通配符 | 含義 | 示例 |
---|---|---|
? | 匹配任意的 單個字符 | /user? → 匹配 /user1 、/users (長度固定) |
* | 匹配任意數量的 字符(0 個或多個) | /user* → 匹配 /user 、/user123 、/user_abc |
** | 匹配任意數量的 目錄層級 | /a/**/b → 匹配 /a/b 、/a/x/b 、/a/x/y/z/b |
【注意】:SpringMVC 中,
**
只能用于/xx/**/yy
的結構中,不能直接寫成/**
放在中間或結尾。?? 正確用法:
@RequestMapping("/admin/**/page")
? 錯誤用法:
@RequestMapping("/admin**page") // 無效 @RequestMapping("**/page") // 報錯
?
4-3、和 @GetMapping / @PostMapping 的關系?
注解 | 等價寫法 |
---|---|
@GetMapping("/xxx") | @RequestMapping(value="/xxx", method=RequestMethod.GET) |
@PostMapping("/xxx") | @RequestMapping(value="/xxx", method=RequestMethod.POST) |
@DeleteMapping | 用于 DELETE 請求 |
@PutMapping | 用于 PUT 請求 |
所以在 RESTful 風格中,推薦使用 @GetMapping
、@PostMapping
等簡化注解。
@RequestMapping的派生注解
【注意】:
????????瀏覽器只能處理get和post請求,即使form表單有method屬性,可以指定請求方式,也只有get和post兩種!(put和delete請求怎么處理,見后面的內容)
?
4-4、springmvc支持路徑中的占位符
這是 RESTful 風格中非常核心的一項功能。
SpringMVC 支持通過
{}
的形式在請求路徑中定義占位符,然后通過@PathVariable
注解將 URL 中的路徑變量綁定到方法參數上。
舉個例子說明:
@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/detail/{id}")public String getUserById(@PathVariable("id") String id) {System.out.println("查詢用戶 ID: " + id);return "user_detail";}
}
如果你訪問:
http://localhost:8080/user/detail/123
就會打印:
查詢用戶 ID: 123
1、語法說明:
用法 | 說明 |
---|---|
/user/{id} | {id} 是路徑中的占位符 |
@PathVariable("id") | 將 {id} 的值綁定到參數上 |
2、常見變種:
(1)多個路徑變量
@RequestMapping("/user/{userId}/order/{orderId}")
public String getOrder(@PathVariable("userId") String uid,@PathVariable("orderId") String oid) {// ...
}
訪問:
/user/10/order/555
就會自動綁定:
-
uid = "10"
-
oid = "555"
?
(2)占位符名和參數名一致時,@PathVariable 可以省略參數名
@RequestMapping("/user/{id}")
public String getUserById(@PathVariable String id) {// OK,變量名和參數名一致
}
?
(3)占位符 + 通配符
@RequestMapping("/file/{path}/**")
public String getFile(@PathVariable String path) {// 捕捉一部分路徑,同時保留通配符
}
匹配以
/file/xxx/...
開頭的所有路徑,xxx
這一段會被當作路徑變量path
,**
表示后面還有任意多層路徑。?
?
(4)動態 RESTful 風格操作
你可以用路徑來表示操作行為,例如:
// 刪除用戶
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public void deleteUser(@PathVariable int id) {// DELETE 操作
}
配合前端的 RESTful 請求或 Postman 測試非常方便。
?
3、與 @RequestParam 的區別
對比項 | @PathVariable | @RequestParam |
---|---|---|
來源 | URL 的路徑部分 | URL 的查詢參數部分 |
示例 | /user/123 | /user?id=123 |
用法 | @PathVariable("id") | @RequestParam("id") |
五、SpringMVC獲取請求參數
5-1、servletapi獲取請求參數(不推薦)
原生的獲取方法,從HttpServletRequest中獲取請求參數
請求參數通常來自:
-
URL 查詢參數:例如
/login?username=Tom&password=123
-
表單提交(POST):例如
<form>
提交的數據 -
請求體(body):JSON、XML 等格式的原始數據
1、最常用的方式:getParameter()
示例:
<form th:action="@{/testParams/testRequestParams}" method="post">username: <input type="text" name="username"><br>password: <input type="text" name="password"><input type="submit" value="測試testRequestParams"></form>
@PostMapping("/testRequestParams")public String testRequestParams(HttpServletRequest request){String username = request.getParameter("username");String password = request.getParameter("password");System.out.println("username = " + username + "; password = " + password);return "success";}
-
無論是 GET 還是 POST 請求都可以使用
-
只能獲取 key=value 結構的表單數據或 URL 參數
-
不能處理 JSON 請求體數據!!!
?【注意】:
1、HttpServletRequest中的值是DispatcherServlet放入的。
2、這是原生的獲取請求參數的方法,SpringMVC已經做了封裝,不再建議使用!
5-2、通過控制器獲取請求參數
直接,將Controller類中的方法里面的形參的參數名 = 請求參數的參數名
示例:
<form th:action="@{/testParams/testControllerParams}" method="post">username: <input type="text" name="username"><br>password: <input type="text" name="password"><input type="submit" value="測試testRequestParams"></form>
【備注】:當有多個同名的請求參數的時候,比如:復選框
<form th:action="@{/testParams/testControllerParams}" method="post">username: <input type="text" name="username"><br>password: <input type="text" name="password"><br>hobby: <input type="checkbox" name="hobby" value="a">a<input type="checkbox" name="hobby" value="b">b<input type="checkbox" name="hobby" value="c">c<br><input type="submit" value="測試testRequestParams"></form>
1、直接使用String獲取(多個值逗號分離)
2、使用String[]獲取
5-3、使用 @RequestParam
—— 獲取URL參數或表單參數?
@RequestParam是將請求參數和控制器方法的形參創建映射關系。
示例:
@PostMapping("/login")
public String login(@RequestParam("username") String username,@RequestParam("password") String password) {System.out.println(username + " - " + password);return "success";
}
此時可以解決表單請求的參數名和controller方法里面的形參名不一致的問題!
@RequestParam
不能用來直接獲取 JSON 格式的請求體數據。
?
@RequestParam注解一共有3個屬性:
用法 | 含義 |
---|---|
@RequestParam("xxx") | 從請求中取出名為 xxx 的參數 |
required = false | 表示該參數可以為空,默認是 true |
defaultValue | 沒傳參數,或傳參為“”時,用默認值(和required無關! ) |
5-4、@RequestHeader
@RequestHeader是將請求頭信息和控制器方法的形參創建映射關系
@RequestHeader注解一共有三個屬性:value、required、defaultValue,用法同@RequestParam
示例:
@GetMapping("/header")
public String testHeader(@RequestHeader("User-Agent") String userAgent) {System.out.println("客戶端瀏覽器信息:" + userAgent);return "ok";
}
如果你想獲取所有請求頭,可以使用 HttpServletRequest
或 @RequestHeader Map
:
@GetMapping("/allHeaders")
public String allHeaders(@RequestHeader Map<String, String> headers) {headers.forEach((k, v) -> System.out.println(k + ": " + v));return "ok";
}
?使用場景舉例:
場景 | 請求頭 | 用法 |
---|---|---|
判斷瀏覽器類型 | User-Agent | 適配頁面或功能 |
語言國際化 | Accept-Language | 根據瀏覽器語言返回不同語言的頁面 |
認證 | Authorization | Token 認證、JWT 登錄 |
CORS 請求 | Origin 、Referer | 用于跨域校驗或來源過濾 |
5-5、@CookieValue
@CookieValue是將cookie數據和控制器方法的形參創建映射關系
@CookieValue注解一共有三個屬性:value、required、defaultValue,用法同@RequestParam。
示例:
@Controller
public class CookieController {@RequestMapping("/readCookie")@ResponseBodypublic String readCookie(@CookieValue("username") String username,@CookieValue(value = "token", defaultValue = "no-token") String token) {return "用戶名:" + username + ",Token:" + token;}
}
5-6、通過pojo獲取請求參數
通過 POJO(JavaBean)對象接收請求參數
相比一個個用 @RequestParam
接收參數,使用 POJO 可以自動封裝多個參數,非常方便、清晰、結構化。
1、什么是 POJO 獲取請求參數?
SpringMVC 會自動根據前端傳來的請求參數名/URL參數,將它們填充進你定義的 JavaBean(POJO)對象中。
2、使用條件
只要滿足下面兩個條件,SpringMVC 就可以自動封裝:
-
請求參數名(form 中的 name)/URL參數 和 JavaBean 的屬性名相同
-
JavaBean 有 無參構造器 + setter 方法
3、示例
1. 定義 POJO 類(如 User.java)
public class User {private String username;private String password;private String role;// 必須要有 setter 和 getterpublic String getUsername() { return username; }public void setUsername(String username) { this.username = username; }public String getPassword() { return password; }public void setPassword(String password) { this.password = password; }public String getRole() { return role; }public void setRole(String password) { this.role = role; }
}
2. 前端 HTML 表單
<form action="/user/login?role=admin" method="post">用戶名:<input type="text" name="username"><br>密碼:<input type="password" name="password"><br><input type="submit" value="登錄">
</form>
3. Controller 中用 POJO 接收參數
@Controller
@RequestMapping("/user")
public class UserController {@PostMapping("/login")public String login(User user) {// Spring 會自動把參數封裝到 user 對象里System.out.println("用戶名: " + user.getUsername());System.out.println("密碼: " + user.getPassword());System.out.println("角色: " + user.getRole());return "success";}
}
【注意】:省略的是:@ModelAttribute注解,SpringMVC 會默認使用,只要參數名匹配!
它的解析順序大致如下:
先從請求 URL 中找參數
再從表單字段(POST)中找參數
兩邊都有,就以表單字段為準(POST 參數優先)
找到的參數會通過 JavaBean 的
setXxx()
注入到對象里
4、支持嵌套對象封裝
你可以封裝嵌套對象,比如:
public class Student {private String name;private Address address;// getter/setter ...
}public class Address {private String city;private String street;// getter/setter ...
}
表單字段名需要使用點語法(Spring 自動識別):
<input name="name">
<input name="address.city">
<input name="address.street">
5、支持集合類型(List/Map)
如果你的類中有:
private List<String> hobbies;
你可以使用以下方式提交:
<input name="hobbies" value="唱歌">
<input name="hobbies" value="跳舞">
Spring 會自動將它封裝成 List。
6、完整示例
功能目標
-
嵌套封裝:學生有地址信息(
Student
包含Address
) -
List 集合封裝:學生可以選多個興趣愛好(
List<String>
) -
表單提交 → SpringMVC Controller → 自動封裝 POJO → 打印輸出
(1)、定義 POJO 類
Address.java
public class Address {private String city;private String street;// getter 和 setter 必須有public String getCity() { return city; }public void setCity(String city) { this.city = city; }public String getStreet() { return street; }public void setStreet(String street) { this.street = street; }
}
Student.java
import java.util.List;public class Student {private String name;private Address address;private List<String> hobbies;public String getName() { return name; }public void setName(String name) { this.name = name; }public Address getAddress() { return address; }public void setAddress(Address address) { this.address = address; }public List<String> getHobbies() { return hobbies; }public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
}
(2)、HTML 表單(studentForm.jsp
)
<form action="/student/register" method="post">姓名:<input type="text" name="name"><br>城市:<input type="text" name="address.city"><br>街道:<input type="text" name="address.street"><br>愛好:<br><input type="checkbox" name="hobbies" value="唱歌">唱歌<br><input type="checkbox" name="hobbies" value="跳舞">跳舞<br><input type="checkbox" name="hobbies" value="籃球">籃球<br><input type="submit" value="提交">
</form>
(3)、SpringMVC Controller(StudentController.java
)
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;@Controller
@RequestMapping("/student")
public class StudentController {@PostMapping("/register")public String register(Student student) {// 輸出接收到的學生信息System.out.println("姓名: " + student.getName());if (student.getAddress() != null) {System.out.println("城市: " + student.getAddress().getCity());System.out.println("街道: " + student.getAddress().getStreet());}if (student.getHobbies() != null) {System.out.println("愛好: ");for (String h : student.getHobbies()) {System.out.println(" - " + h);}}return "success"; // 視圖名稱}
}
【注意】:
此時獲取到的hobby的value是亂碼的!因為是中文。
7、設置請求對象編碼
亂碼問題分為兩種:
- get請求的亂碼;
- post請求的亂碼。
?
(1)、get請求的亂碼
GET 請求的參數是放在 URL 地址欄里的,例如:http://localhost:8080/app?name=張三
這些參數在到達 Servlet
之前,Tomcat 已經解析好了,此時用的默認編碼(早期是 ISO-8859-1
),你設置編碼已來不及,亂碼已經發生。
所以這是tomcat的問題,需要在tomcat的server.xml
中明確指定:
{Tomcat目錄}/conf/server.xml
加上
URIEncoding="UTF-8"
就可以防止 GET 請求中文亂碼。?
(2)、post請求亂碼
請求類型 | 參數位置 | 解決方式 |
---|---|---|
POST | 請求體 body | 使用 request.setCharacterEncoding("UTF-8") |
GET | URL 地址欄 | 設置 Tomcat 的 URIEncoding="UTF-8" |
【注意點】:必須在第一次獲取參數之前設置編碼!
一旦調用了以下任意方法:
request.getParameter()
request.getParameterMap()
request.getParameterNames()
request.getParameterValues()
?Servlet 容器就會立即解析請求體,并把參數緩存起來。
如果你在調用之后再設置編碼,就無效了,編碼已經決定,不會重新解析請求體。
? 錯誤做法(設置編碼太晚)
String name = request.getParameter("name"); // 已經觸發解析
request.setCharacterEncoding("UTF-8"); // ? 已經沒用了!
? 正確做法(先設置編碼)
request.setCharacterEncoding("UTF-8"); // ? 先設置編碼
String name = request.getParameter("name"); // 然后獲取參數
但是應為此時我們用的是pojo獲取請求參數,使用request.setCharacterEncoding("UTF-8");是無效的,應為dispatcherServlet已經解析了參數了!
所以用過濾器統一處理,因為,過濾器在servlet之前執行!
在web.xml中配置如下過濾器:
<filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><!-- 請求設置編碼 --><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><!-- 響應設置編碼 --><init-param><param-name>forceResponseEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
5-7、【回顧】使用 @PathVariable
(獲取 REST 風格路徑參數)
@GetMapping("/user/{id}")
public String getUserById(@PathVariable("id") int id) {System.out.println("id = " + id);return "user";
}
5-8、使用 @RequestBody
(接收 JSON 數據)
@PostMapping("/user")
public String createUser(@RequestBody User user) {System.out.println("user = " + user);return "success";
}
-
會從 請求體中讀取 JSON,然后通過 Jackson 自動反序列化成
User
對象。 -
需要前端設置
Content-Type: application/json
。
需要前端發送 Content-Type: application/json
請求頭,示例:
<!-- requestbody.html -->
<script>function register() {const user = {username: "Alice",email: "alice@example.com",password: "123456"};fetch("/json/register", {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify(user)}).then(response => {if (response.ok) {alert("Registered successfully!");}});}
</script><button onclick="register()">Register via JSON</button>
五、SpringMVC 中的三種域對象
request
、session
、和servletContext
(application),它們統稱為 “三大作用域對象”。
5-1、什么是域對象?
域對象(也叫作用域對象)是 Web 中用來 在不同范圍內共享數據 的容器。?
域對象 | 生命周期范圍 | 使用場景 |
---|---|---|
request | 一次請求內有效 | 請求轉發、表單處理 |
session | 一個用戶會話內有效 | 登錄狀態、用戶信息保存 |
servletContext | 整個 Web 應用范圍內都有效(太大) | 全局參數、在線人數統計等 |
?
1、區分:什么是一“次請求”,什么是一“次會話”
(1)、?什么是一“次請求”
一次請求 = 瀏覽器或客戶端 發出的一次 HTTP 請求。它從你訪問某個 URL 的那一刻開始,到服務器響應并返回結果為止。
判斷是不是“一次請求”:你可以在控制器方法中打印 request 對象的地址或哈希值:
@RequestMapping("/test")
public String test(HttpServletRequest request) {System.out.println("request hash: " + request.hashCode());return "success";
}
如果你在多個方法里都打了這個語句,只有同一個請求轉發時(比如 forward:/page
),request.hashCode()
是相同的。?
【注意】:
頁面刷新或重新點擊鏈接 → 就是新的請求!!!
即使訪問的是同一個地址,但每點擊一次或刷新一次,就是一個新的 request。
特點:生命周期短
-
請求來了就創建
-
響應完成就銷毀
-
不同請求之間,
request
互不影響
(2)、一次會話(session)?
一次會話 = 用戶打開瀏覽器 → 訪問網站 → 關閉瀏覽器或長時間不操作 → 會話結束。
瀏覽器不關、用戶不斷操作,這個會話就一直保持。
判斷是不是“一次會話”:打印 session ID
@RequestMapping("/checkSession")
public String checkSession(HttpSession session) {System.out.println("session ID: " + session.getId());return "session";
}
-
多次訪問
/checkSession
,只要瀏覽器沒關,session ID 不變,說明是同一個會話。 -
如果你關掉瀏覽器再訪問,或等上20~30分鐘,session ID 就會變,說明是新會話。
【注意】:
域名不同就不是同一個 session!
一般,同一個項目下共享 session!!!
情景:同一瀏覽器訪問兩個項目
http://localhost:8080/app1
http://localhost:8080/app2
雖然端口和瀏覽器一樣,但項目上下文不同(app1 ≠ app2),默認是兩個不同的 session,除非你顯式配置 Cookie 的路徑。
5-2、request
域對象?
特點
-
生命周期:一次請求內有效
-
隨著請求的完成而銷毀(如訪問頁面、轉發等)
-
適合存儲:表單校驗信息、錯誤提示、一次性數據傳遞
1、使用ServletAPI向request域對象共享數據?
示例:
index.html頁面:
<h2>test request scope value</h2>
<a th:href="@{/testRequestByServletApi}">set request attribute and then go to success page</a>
點擊之后發送一次請求:http://localhost:8080/WsSpringMvc/testRequestByServletApi
@Controller
public class ScopeController {@RequestMapping("/testRequestByServletApi")public String testRequestByServletApi(HttpServletRequest request){// 每一個域對象都有三種方法:// setAttribute// getAttribute// removeAttributerequest.setAttribute("testRequestScope", "hello servletAPI");return "successScope";}}
【注意】:
return "success"
是“請求轉發”(forward)。在 Spring MVC 中,當你的控制器方法返回一個字符串(如
"success"
),默認行為是:
Spring 會將這個字符串當作視圖名
然后交給視圖解析器(ViewResolver)來處理
最終會使用請求轉發(forward)到對應的頁面
?等價于:
request.getRequestDispatcher("/WEB-INF/views/success.jsp").forward(request, response);
所以,這是一次請求轉發,而不是重定向。
如果你想“重定向”,需要顯式寫上:
return "redirect:/someOtherPath";
返回值 | 行為 | 地址欄是否變化 | 請求是否共享 request 域 |
---|---|---|---|
"success" | 請求轉發 | ? 不變 | ? 可以用 ${msg} 取值 |
"redirect:/xx" | 重定向 | ? 會變 | ? request 域不共享(已經不是一次請求了!) |
successScope.html頁面
<body><h1>success</h1><p th:text="${testRequestScope}"></p></body>
【注意】:
在 Thymeleaf 中,
"${}"
表達式用于獲取變量,但不同作用域(request、session、application)中的變量訪問方式有所不同。1. Request 域(
HttpServletRequest
):???????<p th:text="${key}"></p>
2. Session 域(
HttpSession
)<p th:text="${session.key}"></p>
3. Application 域(
ServletContext
)<p th:text="${application.key}"></p>
2、使用ModelAndView向request域對象共享數據(Springmvc)?
下面的2,3,4,5方式,都是Springmvc中提供的向request域對象共享數據的方法,最終原碼都是包裝成一個ModelAndView對象!
ModelAndView有model和view的功能:
- model主要用于向請求域共享數據;
- view用于設計視圖,實現頁面跳轉!
示例:
@RequestMapping("/testModelAndViewApi")public ModelAndView testModelAndViewApi(){ModelAndView modelAndView = new ModelAndView();// 處理model數據:即,向請求域request共享數據modelAndView.addObject("testRequestScopeModelAndView", "hello modelAndView");// 設置視圖名稱modelAndView.setViewName("successScope");return modelAndView;}
【注意】:
返回值一定要是ModelAndView,因為要把視圖返回給視圖解析器!
3、使用Model向request域對象共享數據(Springmvc)??
?
4、使用Map集合向request域對象共享數據(Springmvc)??
map集合一般key就是string類型,value就是object類型!?
5、使用ModelMap集合向request域對象共享數據(Springmvc)???
5-3、Session域對象
使用方法:
@RequestMapping("/login")
public String login(HttpSession session) {session.setAttribute("user", "Tom");return "home";
}
-
獲取 session:
HttpSession session
-
讀取 session 數據:
session.getAttribute("user")
5-4、servletContext
(application)域對象
特點:
-
生命周期:整個 Web 應用運行期間有效
-
所有用戶共享,全局作用域
-
適合存儲:全局配置、網站訪問量、緩存等
application域對象,范圍太大了,用的最多的還是request和session!
使用方法:
@RequestMapping("/app")
public String applicationScope(HttpSession session) {ServletContext context = session.getServletContext();context.setAttribute("count", 100);return "global";
}
也可以直接通過 @Autowired
注入:
@Autowired
ServletContext context;
5-5、總結一句話
你想用什么作用域對象(如 request、session、application),直接寫在控制器方法參數中即可獲取,Spring MVC 會自動注入,不用你手動 new 或額外配置。
示例:
@RequestMapping("/demo")
public String demo(HttpServletRequest request,HttpSession session,ServletContext context) {request.setAttribute("reqData", "數據來自 request 域");session.setAttribute("sessData", "數據來自 session 域");context.setAttribute("appData", "數據來自 application 域");return "success"; // 視圖解析到 success.html 或 JSP
}