一、SpringMVC 的視圖
在 SpringMVC 中,視圖的作用渲染數據,將模型 Model (將控制器(Controller))中的數據展示給用戶。
在 Java 代碼中,視圖由接口 org.springframework.web.servlet.View
表示
SpringMVC 視圖的種類很多,默認有轉發視圖InternalResourceView
和重定向視圖RedirectView
。
當工程引入 Jstl 的依賴,轉發視圖會自動轉換為 JstlView;
若使用的視圖技術為 Thymeleaf,在 SpringMVC 的配置文件中配置了 Thymeleaf 的視圖解析器,由此視圖解析器解析之后所得到的是 ThymeleafView。
1-1、視圖解析流程(基本原理)
當你在控制器中 return "success"
,SpringMVC 會做以下幾步:
把
"success"
當作視圖名(ViewName)使用 視圖解析器(ViewResolver) 解析這個視圖名
返回一個實現了 View 接口的視圖對象(如
InternalResourceView
,JstlView
,ThymeleafView
)調用
view.render(...)
渲染模型數據(model)+ 轉發或重定向
?
1-2、SpringMVC 支持的視圖類型
類型 | 說明 | 關鍵類或說明 |
---|---|---|
轉發視圖 | 默認是 InternalResourceView ,通過 RequestDispatcher.forward() 轉發 | 不改變地址欄,可共享 request 域 |
重定向視圖 | RedirectView ,使用 response.sendRedirect() | 地址欄改變,不能共享 request 域 |
JSTL 視圖 | 加了 JSTL 依賴后,轉發視圖會自動變成 JstlView | 支持 <c:forEach> 等標簽 |
Thymeleaf 視圖 | 配置了 Thymeleaf 解析器后使用 ThymeleafView | 支持 Thymeleaf 模板語法 |
其他 | VelocityView、FreeMarkerView 等 | 可擴展視圖技術 |
?
1-3、Thymeleaf
若是視圖是由Thymeleaf解析器解析之后得到的,獲取到的視圖就是ThymeleafView,但是不是左右的情況,獲取的都是ThymeleafView。
當控制方法中返回的視圖名稱以“forword:”為前綴的時候,創建的是InternalResourceView
當控制方法中返回的視圖名稱以“redirect:”為前綴的時候,創建的是RedirectView
只有視圖名稱沒有任何前綴的時候,創建的才是ThymeleafView。
當控制器方法中所設置的視圖名稱沒有任何前綴時,此時的視圖名稱會被 SpringMVC 配置文件中所配置的視圖解析器解析,視圖名稱拼接視圖前綴和視圖后綴所得到的最終路徑,會通過轉發的方式實現跳轉。
1-4、轉發視圖
1、瀏覽器發送一次請求,瀏覽器中的URL不會變!能獲取到請求域中的數據!
2、web-inf下的資源不能通過瀏覽器獲取到,但是可以通過服務器轉發獲取到!
3、不能跨域
SpringMVC 中默認的轉發視圖是 InternalResourceView
SpringMVC 中創建轉發視圖的情況:
當控制器方法中所設置的視圖名稱以 "forward:" 為前綴時,創建 InternalResourceView 視圖,此時的視圖名稱不會被 SpringMVC 配置文件中所配置的視圖解析器解析,而是會將前綴 "forward:" 去掉,剩余部分作為最終路徑通過轉發的方式實現跳轉。
例如 "forward:/", "forward:/employee"
用的比較少,因為Thymeleaf視圖解析器,默認就是轉發視圖!
1-5、重定向視圖
1、瀏覽器發送兩次請求,瀏覽器中的URL會變!不能獲取到請求域中的數據!
2、可以跨域!
SpringMVC 中默認的重定向視圖是 RedirectView。
當控制器方法中所設置的視圖名稱以 "redirect:" 為前綴時,創建 RedirectView 視圖,此時的視圖名稱不會被 SpringMVC 配置文件中所配置的視圖解析器解析,而是會將前綴 "redirect:" 去掉,剩余部分作為最終路徑通過重定向的方式實現跳轉。
例如 "redirect:/", "redirect:/employee"
1-6、視圖控制器:view-controller
沒有其他控制方法的處理,只需要通過請求映射,返回一個視圖名稱的時候。
此時控制中的所有的請求映射將全部失效!
<mvc:view-controller>
是 Spring XML 配置中的簡潔寫法,專門用于靜態頁面跳轉,適合“無業務邏輯”的場景,現在依然常用,尤其在配置登錄頁、首頁等時非常實用。
在 Spring Boot 中,不再使用 XML 配置 <mvc:view-controller>
,而是使用 Java 配置(基于代碼的方式)來實現相同功能。
目標:實現“請求路徑直接跳轉到某個頁面”,不寫 Controller 方法
比如我們希望:
訪問
/login
→ 顯示login.html
訪問
/
→ 顯示index.html
1、Spring Boot Java 配置方式(推薦寫法)
你只需創建一個配置類,實現 WebMvcConfigurer
接口,重寫 addViewControllers()
方法:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/login").setViewName("login");registry.addViewController("/").setViewName("index");}
}
2、視圖位置說明(默認)
Spring Boot 默認使用的是 Thymeleaf,默認的視圖位置是:
src/main/resources/templates/
所以你要在這里放:
src/main/resources/templates/login.html
src/main/resources/templates/index.html
(如果你用的是 JSP,需要特殊配置)
3、等價于 Spring XML 中的寫法:
<mvc:view-controller path="/login" view-name="login"/>
總結一句話:
在 Spring Boot 中,你可以通過實現
WebMvcConfigurer
接口的 Java 配置類,用addViewControllers()
實現和 XML 中<mvc:view-controller>
一樣的功能 —— 頁面跳轉,無需寫 Controller,簡單又優雅。
二、RESTful設計風格
2-1、什么是 RESTful?
RESTful 是一種 Web 設計風格,不是技術,是一種“使用 URL + HTTP 方法 表達資源操作”的設計規范,常用于 Web API 接口開發。
?
2-2、RESTful 的核心特點
要素 | 示例 | 含義 |
---|---|---|
URL 表示資源 | /user/1 | 表示用戶 ID 為 1 的用戶 |
HTTP 方法表示動作 | GET、POST、PUT、DELETE | 分別對應:查、增、改、刪 |
【注意】:
請求參數以/xxxx的方式拼接在URL中!?
?
2-3、傳統 VS RESTful 路徑對比
功能 | 傳統風格 URL | RESTful 風格 URL |
---|---|---|
查詢用戶 | /user/get?id=1 | GET /user/1 |
添加用戶 | /user/add | POST /user |
修改用戶 | /user/update?id=1 | PUT /user/1 |
刪除用戶 | /user/delete?id=1 | DELETE /user/1 |
?
2-4、Spring MVC 實現 RESTful:案例說明
我們現在用一個簡單的 Spring MVC 項目說明 RESTful 怎么落地實現。
1. RESTful Controller 示例(路徑 + 方法)
package com.example.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;@Controller
@RequestMapping("/user")
public class UserController {// 查詢用戶(GET /user/admin)@GetMapping("/{username}")public String getUser(@PathVariable("username") String username) {System.out.println("查詢用戶:" + username);return "success";}// 添加用戶(POST /user)@PostMappingpublic String addUser(User user) {System.out.println("添加用戶: " + user);return "success";}// 修改用戶(PUT /user/wsbazinga/123)@PutMappingpublic String updateUser(@RequestParam("username") String username,@RequestParam("password") String password) {System.out.println("更新用戶: username = " + username + ", password = " + password);return "success";}// 刪除用戶(DELETE /user/wsbazinga)@DeleteMapping("/{username}")public String deleteUser(@PathVariable("username") String username) {System.out.println("刪除用戶:" + username);return "success";}
}
2-5、前端如何測試不同的 HTTP 方法?
1、post和get請求:
<!-- get請求 --><a th:href="@{/user/admin}">查詢id = 1的用戶</a><br><!-- post請求 --><form th:action="@{/user}" method="post">username: <input type="text" name="username">password: <input type="text" name="password">role: <input type="text" name="role"><input type="submit" value="add"></form>
2、put請求?
瀏覽器默認只能發 GET 和 POST 請求。對于 PUT 和 DELETE,有幾種方法:
方法1:使用 Postman測試
curl -X PUT http://localhost:8080/user/1
curl -X DELETE http://localhost:8080/user/1
方法2:表單中使用 Spring 的隱藏字段 + 過濾器支持
示例:put請求
<form th:action="@{/user}" method="post"><!-- 對用戶沒有意義 --><input type="hidden" name="_method" value="put" />username: <input type="text" name="username">password: <input type="text" name="password">role: <input type="text" name="role"><input type="submit" value="update"></form>
【注意】:
type="hidden":用戶頁面不可見
name = "_method" value="put"才能讓post請求變成put請求!?
并在 web.xml
中配置過濾器:
<!-- 支持RESTful方式提交PUT/DELETE -->
<filter><filter-name>hiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping><filter-name>hiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
3、delete請求?
通過<a>超鏈接,點擊之后,調用form表單的delete方法,走filter執行。
<div id="delete"><h2>form delete request</h2><a th:href="@{/user/wsbainga123}" @click="deleteUser">delete user</a><form id="deleteForm" method="post"><!-- 對用戶沒有意義 --><input type="hidden" name="_method" value="delete" /></form></div><!-- 引入 Vue --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>new Vue({el: '#delete',methods:{deleteUser: function (event) {// 獲取form表單var deleteForm = document.getElementById("deleteForm");// 添加form表單的action屬性,若是沒有,默認提交到當前頁面// form表單的action屬性 = 點擊事件<a>的href的值deleteForm.action = event.target.href;// 提交表單deleteForm.submit();// 取消超鏈接的默認行為(跳轉頁面)event.preventDefault();}}});</script>
2-6、總結一句話:
RESTful 是一種使用“URL 表示資源 + HTTP 方法表示動作”的設計風格。Spring MVC 通過
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
注解完美支持 RESTful,非常適合寫清晰簡潔的接口。
2-7、web.xml中多個過濾器
此時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><!-- 支持RESTful方式提交PUT/DELETE --><filter><filter-name>hiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping><filter-name>hiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
一定要是處理編碼的過濾器在前!否則,中衛亂碼,因為?hiddenHttpMethodFilter 中會先獲取參數_method,導致處理編碼的過濾器失效!
springmvc項目的web.xml文件內容,基本上就是:一個servlet+兩個過濾器
三、default-servlet-handler配置
在 Spring MVC 中,default-servlet-handler
是一個非常關鍵但經常被忽略的配置,它的作用是:讓靜態資源(如圖片、CSS、JS 等)能夠被正常訪問。
?
3-1、為什么需要 default-servlet-handler
?
Spring MVC 的核心思想是:把所有請求都交給前端控制器 DispatcherServlet
統一處理。
比如你在 web.xml
中通常是這樣配置的:
<servlet-mapping><servlet-name>springDispatcher</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
這意味著:所有請求(包括靜態資源)都會被 SpringMVC 接管!
?? 問題:
SpringMVC 的 DispatcherServlet
默認只處理動態請求(Controller 處理的),對像 /css/style.css
、/js/app.js
這樣的靜態資源它是不會處理的,所以最終就 404 了。
3-2、<mvc:default-servlet-handler>
的作用
該配置的作用是:
告訴 SpringMVC:你不處理靜態資源,請把它們交回給默認的 Servlet(即 Tomcat 的 DefaultServlet)去處理。
處理順序:
1、先交給dispatherServlet處理;
2、找不到,交給defaultServlet處理;
3、找不到,返回404!
示例配置:在springMVC.xml文件中配置
<mvc:default-servlet-handler />
配合使用 DispatcherServlet
的 /
映射時效果最好:
<servlet-mapping><servlet-name>springDispatcher</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
【注意】:
?
這兩個注解要一起使用,要是只配置了
default-servlet-handler,所有的請求將交給default-servlet-handler處理!包括動態請求!
3-3、補充:Spring Boot 中怎么處理靜態資源?
Spring Boot 不需要配置 <mvc:default-servlet-handler>
,它默認自動處理靜態資源,資源路徑為:
src/main/resources/static/
src/main/resources/public/
src/main/resources/resources/
3-4、WEB-INF下的靜態資源
即使你配置了 <mvc:default-servlet-handler/>
,如果靜態資源(如 .html
、.css
、圖片等)放在 WEB-INF
文件夾下,瀏覽器依然無法直接訪問!
Web 容器(如 Tomcat)會自動阻止用戶直接訪問 WEB-INF
及其子目錄的任何資源,這是 Java Web 的安全機制。?
正確放靜態資源的位置:
你應該把 HTML、CSS、JS、圖片 等資源放在這些位置:
放置路徑 | 是否能被訪問 | 說明 |
---|---|---|
/webapp/static/ | ? 可以訪問 | 推薦:SpringMVC 中常用 |
/webapp/public/ | ? 可以訪問 | Tomcat 支持的默認靜態目錄 |
/webapp/css/ 、/js/ | ? 可以訪問 | 可以自由命名 |
/WEB-INF/ | ? 不能訪問 | SpringMVC 和瀏覽器都無法直接訪問 |
示例項目結構(推薦):
src
└── main└── webapp├── index.html ? 可訪問:http://localhost:8080/項目名/index.html├── css/│ └── style.css ? 可訪問:http://localhost:8080/項目名/css/style.css├── js/└── WEB-INF/└── views/└── page.jsp ? 只能通過控制器轉發訪問,不能直接訪問
那 WEB-INF
的作用是什么?
用來存放 JSP 文件(如視圖頁面)
保證用戶不能通過 URL 直接訪問它們
必須通過控制器轉發才能訪問,如:
3-5、總結:
在 Spring MVC 中,
<mvc:default-servlet-handler />
是用于將 靜態資源請求交還給 Web 容器(如 Tomcat)默認處理的關鍵配置,如果你使用了/
映射 DispatcherServlet,就必須加它,否則 CSS/JS/圖片可能無法加載。