第一章:SpringMVC環境搭建與基礎配置
1.1 Maven依賴配置
在Maven項目中,SpringMVC的依賴配置是開發的第一步。根據Spring官方推薦,以下是SpringMVC 5.3.x版本的Maven依賴配置:
<dependencies><!-- Spring MVC核心依賴 --><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.3.25</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmv</artifactId><version>5.3.25</version></dependency><!-- Servlet API依賴 --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- JSP頁面需要 --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-jsp</artifactId><version>2.2.1</version><scope>provided</scope></dependency><!-- 數據庫連接 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><!-- 其他常用依賴 --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.25</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.3</version></dependency>
</dependencies>
注意:提供的依賴
表示這些依賴由Servlet容器(如Tomcat)提供,不需要打包到最終的WAR文件中。
1.2 web.xml配置
web.xml是Java Web應用的配置中心,需要配置SpringMVC的核心組件——DispatcherServlet :
<web-app xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"version="2.5"><!-- 配置SpringMVC的前端控制器 --><servlet><servlet-name>springDispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 設置配置文件位置 --><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring-mvc-servlet.xml</param-value></init-param><!-- 服務器啟動時加載 --><load-on-startup>1</load-on-startup></servlet><!-- 配置DispatcherServlet的映射路徑 --><servlet-mapping><servlet-name>springDispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 配置ContextLoaderListener加載根應用上下文 --><context-param><param-name>contextClass</param-name><param-value>org.springframework.web.context.support安娜imationConfigWebApplicationContext</param-value></context-param><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param>< listener >< listener-class > org.springframework.web.context.ContextLoaderListener </ listener-class ></ listener >
</web-app>
關鍵配置說明:
DispatcherServlet
是SpringMVC的前端控制器,負責接收所有HTTP請求并分發給對應的處理器contextConfigLocation
參數指定了SpringMVC的配置文件位置<url-pattern>/</url-pattern>
表示攔截所有請求ContextLoaderListener
用于加載根應用上下文(applicationContext.xml),通常包含業務邏輯和數據訪問層的配置
1.3 SpringMVC核心配置文件
在/WEB-INF/
目錄下創建spring-mvc-servlet.xml
配置文件,配置SpringMVC的核心組件:
<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"><!-- 開啟組件掃描 --><context:component-scan base-package="com.example.controller"/><!-- 開啟注解驅動 --><mvc:annotation-driven/><!-- 配置視圖解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/"/><property name="suffix" value=".jsp"/></bean><!-- 處理靜態資源 --><bean class="org.springframework.web.servlet.view資源處理器" id="resourceProcessor"><property name="locations"><list><value>/static/</value></list></property><property name="mappings"><list><value>/js/**</value><value>/css/**</value><value>/images/**</value></list></property></bean><!-- 配置文件上傳解析器 --><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="UTF-8"/><property name="maxUploadSize" value="10485760"/><property name="maxInMemorySize" value="1048576"/></bean>
</beans>
關鍵配置說明:
component-scan
開啟組件掃描,自動發現并注冊帶有@Controller
等注解的類annotation-driven
開啟注解驅動,支持@RequestMapping
等注解InternalResourceViewResolver
配置JSP視圖解析器,指定視圖頁面的路徑前綴和后綴ResourceHandler
處理靜態資源請求,避免DispatcherServlet攔截靜態資源CommonsMultipartResolver
配置文件上傳解析器,用于處理文件上傳請求
1.4 第一個SpringMVC示例(HelloWorld)
創建一個簡單的HelloWorld控制器,演示SpringMVC的基本工作流程:
package com.example.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;import java.util.Date;// 標記為控制器
@Controller
public class HelloController {// 處理GET請求,映射到/hello路徑@RequestMapping(value = "/hello", method = RequestMethod.GET)public String helloGET() {// 直接返回視圖名稱,視圖解析器會自動解析為/WEB-INF/views/hello.jspreturn "hello";}// 返回JSON數據@RequestMapping(value = "/hello/json", method = RequestMethod.GET)@ResponseBodypublic String helloJSON() {return "{\"message\":\"Hello Spring MVC!\"}";}// 返回ModelAndView,可以攜帶模型數據@RequestMapping("/hello/model")public ModelAndView helloModel() {// 創建ModelAndView對象ModelAndView mav = new ModelAndView();// 添加模型數據mav.addObject("message", "Hello Spring MVC!");mav.addObject("time", new Date());// 設置視圖名稱mav.setViewName("hello");return mav;}
}
在/WEB-INF/views/
目錄下創建hello.jsp
頁面:
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>SpringMVC Hello World</title>
</head>
<body><h1><%= request.getAttribute("message") %></h1><p>Current Time: <%= request.getAttribute("time") %></p>
</body>
</html>
運行測試: 啟動Tomcat服務器后,在瀏覽器中訪問以下路徑:
/hello
:顯示JSP頁面/hello/json
:返回JSON數據/hello/model
:顯示攜帶模型數據的JSP頁面
第二章:核心注解與請求映射
2.1 @Controller與@RequestMapping
@Controller注解是SpringMVC的核心注解,用于標記一個類為控制器:
@Controller
public class UserController {// ...
}
@RequestMapping注解用于映射HTTP請求到控制器方法:
@Controller
public class UserController {// 映射到/user路徑的GET請求@RequestMapping(value = "/user", method = RequestMethod.GET)public String getUser() {return "user";}// 映射到/user的POST請求@RequestMapping(value = "/user", method = RequestMethod.POST)public String createUser() {return "createUser";}
}
2.2 @GetMapping、@PostMapping等請求方法注解
SpringMVC 4.3+版本引入了更簡潔的請求方法注解:
@RestController
public class UserController {// 處理GET請求,等價于@RequestMapping(method = GET)@GetMapping("/user")public String getUser() {return "GET User";}// 處理POST請求,等價于@RequestMapping(method = POST)@PostMapping("/user")public String createUser() {return "POST User";}// 處理PUT請求@PutMapping("/user/{id}")public String updateUser(@PathVariable Long id) {return "PUT User " + id;}// 處理DELETE請求@DeleteMapping("/user/{id}")public String.deleteUser(@PathVariable Long id) {return "DELETE User " + id;}
}
注解對比:
注解 | 對應的HTTP方法 | 使用場景 |
---|---|---|
@GetMapping | GET | 獲取資源信息 |
@PostMapping | POST | 創建新資源 |
@PutMapping | PUT | 更新資源 |
@DeleteMapping | DELETE | 刪除資源 |
@PatchMapping | PATCH | 部分更新資源 |
@RequestMapping | 任意方法 | 更靈活的請求映射 |
2.3 路徑變量與請求參數綁定(@PathVariable、@RequestParam)
@PathVariable用于綁定URL中的路徑變量:
@RestController
public class UserController {// 映射到/user/123路徑,將123綁定到id參數@GetMapping("/user/{id}")public String getUserById(@PathVariable Long id) {return "User ID: " + id;}// 多個路徑變量@GetMapping("/products/{category}/{id}")public String Product(@PathVariable String category, @PathVariable Long id) {return "Category: " + category + ", ID: " + id;}// 名稱匹配(參數名與路徑變量名相同)@GetMapping("/user/{userId}")public String getUser(@PathVariable Long userId) {return "User ID: " + userId;}// 顯式指定名稱@GetMapping("/user/{userId}")public String getUser(@PathVariable("userId") Long id) {return "User ID: " + id;}
}
@RequestParam用于綁定請求參數(查詢參數或請求體參數):
@RestController
public class UserController {// 獲取查詢參數,如?name=Alice@GetMapping("/user/param")public String getUserByParam(@RequestParam String name) {return "Hello, " + name + "!";}// 指定參數名@GetMapping("/user/named")public String getUserByName(@RequestParam("name") String-username) {return "Hello, " +-username + "!";}// 可選參數,默認值@GetMapping("/user/optional")public String getUserOptional(@RequestParam(required = false, defaultValue = "Guest") String name) {return "Hello, " + name + "!";}// 處理多個參數@GetMapping("/user/multiple")public String getUserMultiple(@RequestParam String name,@RequestParam Integer age) {return "Name: " + name + ", Age: " + age;}
}
前端測試:
/user/param?name=Alice
:返回"Hello, Alice!"/user/multiple?name=Bob&age=25
:返回"Name: Bob, Age: 25"
2.4 請求頭與Cookie綁定(@RequestHeader、@CookieValue)
@RequestHeader用于綁定HTTP請求頭參數:
@RestController
public class HeaderController {// 獲取User-Agent請求頭@GetMapping("/user/agent")public String getUserAgent(@RequestHeader("User-Agent") String agent) {return "User-Agent: " + agent;}// 指定請求頭參數名@GetMapping("/userCustom")public String getUserCustom(@RequestHeader("X-Custom-Header") String customHeader) {return "Custom Header: " + customHeader;}// 可選請求頭,默認值@GetMapping("/user/optional-header")public String getUserOptionalHeader(@RequestHeader(required = false, defaultValue = "default-value") String header) {return "Header: " + header;}
}
@CookieValue用于綁定Cookie參數:
@RestController
public class CookieController {// 獲取JSESSIONID Cookie@GetMapping("/show-session-id")public String showSessionId(@CookieValue("JSESSIONID") String-sessionId) {return "Session ID from cookie: " +-sessionId;}// 獲取自定義Cookie,可選@GetMapping("/show-custom-cookie")public String showCustomCookie(@CookieValue(required = false, defaultValue = "not-set") String userCookie) {return "User Cookie: " + userCookie;}
}
前端測試:
- 訪問
/show-session-id
:返回當前會話的JSESSIONID - 訪問
/show-custom-cookie
:返回默認值"not-set"(若未設置該Cookie)
第三章:數據綁定與參數傳遞
3.1 基本類型參數傳遞
直接參數綁定:當請求參數名稱與控制器方法參數名稱相同時,SpringMVC可以自動綁定 :
@RestController
public class BasicParamController {// GET請求:/basic?name=Alice&age=25@GetMapping("/basic")public String basicParams(String name, int age) {return "Name: " + name + ", Age: " + age;}// POST請求:表單參數name=Alice&age=25@PostMapping("/basic/post")public String basicPostParams(String name, int age) {return "POST Name: " + name + ", POST Age: " + age;}// 使用@RequestParam顯式指定參數名@GetMapping("/basic/named")public String namedParams(@RequestParam("name") String-username,@RequestParam("age") int userAge) {return "Username: " +-username + ", User Age: " + userAge;}
}
3.2 對象參數綁定(POJO)
POJO參數綁定:當請求參數名稱與POJO的屬性名稱相同時,SpringMVC可以自動將參數綁定到對象的屬性上:
// 定義POJO類
@Data
public class User {private String name;private int age;private String email;// getter和setter方法
}@RestController
public class PojoController {// GET請求:/pojo?name=Alice&age=25&email=alice@example.com@GetMapping("/pojo")public String pojoGet(User user) {return "Name: " + user.getName() + ", Age: " + user.getAge();}// POST請求:表單參數name=Alice&age=25&email=alice@example.com@PostMapping("/pojo/post")public String pojoPost(User user) {return "POST Name: " + user.getName() + ", POST Age: " + user.getAge();}// 處理嵌套對象@Datapublic class Address {private String city;private String street;}@Datapublic class UserWithAddress {private User user;private Address address;}// GET請求:/nested?user.name=Alice&user.age=25&address.city=Beijing@GetMapping("/nested")public String nestedPojo(UserWithAddress userWithAddress) {return "Name: " + userWithAddress.getUser().getName() +", City: " + userWithAddress().getCity();}
}
前端測試:
/pojo?name=Alice&age=25
:返回"Name: Alice, Age: 25"/嵌套?user.name=Bob&user.age=30&address.city=Shanghai
:返回"Name: Bob, City: Shanghai"
3.3 數組與集合參數傳遞
數組參數綁定:通過重復參數名傳遞數組:
@RestController
public class ArrayController {// GET請求:/array?ids=1&ids=2&ids=3@GetMapping("/array")public String arrayParams(@RequestParam int[] ids) {return "IDs: " + Arrays.toString(ids);}// 使用Spring的數組格式(如:ids=1,2,3)@GetMapping("/array/comma-separated")public String commaSeparatedParams(@RequestParam String ids) {return "IDs: " + ids;}
}
集合參數綁定:使用List
或Set
接收參數:
@RestController
public class ListController {// GET請求:/list?names=John&names=Jane&names=Jim@GetMapping("/list")public String listParams(@RequestParam List<String> names) {return "Names: " + names;}// 使用Spring的集合格式(如:names=John,Jane,Jim)@GetMapping("/list/comma-separated")public String commaSeparatedList(@RequestParam String names) {return "Names: " + names;}// 使用JSON傳遞集合@PostMapping("/list/json")public String jsonList(@RequestBody List<String> names) {return "JSON Names: " + names;}
}
前端測試:
/array?ids=1&ids=2&ids=3
:返回"IDs: [1, 2, 3]"/list?names=John&names=Jane
:返回"Names: [John, Jane]"- 使用Postman發送POST請求到
/list/json
,請求體為["Alice", "Bob"]
,返回"JSON Names: [Alice, Bob]"
3.4 文件上傳參數綁定(@RequestParam MultipartFile)
文件上傳配置:在web.xml中添加 CommonsMultipartResolver
:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons CommonsMultipartResolver"><property name="defaultEncoding" value="UTF-8"/><property name="maxUploadSize" value="10485760"/河邊><property name="maxInMemorySize" value="1048576"/>
</bean>
文件上傳控制器 :
@RestController
public class FileUploadController {// 單文件上傳@PostMapping("/upload")public String uploadSingleFile(@RequestParam("file") MultipartFile file) {try {// 保存文件到服務器String path = "/tmp/uploads/" + file.getOriginalFilename();file transferTo(new File(path));return "File上傳成功: " + file.getOriginalFilename();} catch (Exception e) {return "File上傳失敗: " + e.getMessage();}}// 多文件上傳@PostMapping("/uploadMultiple")public String uploadMultipleFiles(@RequestParam("files") List<MultipartFile> files) {try {for (MultipartFile file : files) {String path = "/tmp/uploads/" + file.getOriginalFilename();file transferTo(new File(path));}return "多文件上傳成功: " + files.size() + " files";} catch (Exception e) {return "多文件上傳失敗: " + e.getMessage();}}// 帶其他參數的文件上傳@PostMapping("/upload/with-params")public String uploadWithParams(@RequestParam("file") MultipartFile file,@RequestParam("username") String username) {try {// 保存文件到服務器String path = "/tmp/uploads/" + username + "-" + file.getOriginalFilename();file transferTo(new File(path));return "File上傳成功: " + file.getOriginalFilename();} catch (Exception e) {return "File上傳失敗: " + e.getMessage();}}
}
HTML表單 :
<!-- 單文件上傳 -->
<form action="/upload單車" method="POST"enctype="multipart/form-data"><input type="file" name="file"><input type="submit" value="上傳">
</form><!-- 多文件上傳 -->
<form action="/upload多" method="POST"enctype="multipart/form-data"><input type="file" name="files" multiple><input type="submit" value="上傳">
</form><!-- 帶其他參數的文件上傳 -->
<form action="/upload/with-params" method="POST"enctype="multipart/form-data"><input type="text" name="username" placeholder="用戶名"><input type="file" name="file"><input type="submit" value="上傳">
</form>
注意事項:
- 文件上傳需要設置
enctype="multipart/form-data"
- 需要添加
commons-fileupload
和commons-io
依賴 :
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version>
</dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.5</version>
</dependency>
第四章:視圖解析與模板引擎集成
4.1 ViewResolver配置與視圖返回
視圖解析器負責將控制器返回的邏輯視圖名解析為物理視圖 :
// 配置JSP視圖解析器
@Bean
public ViewResolver jspViewResolver() {InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix("/WEB-INF/views/"); // 視圖前綴resolver.setSuffix(".jsp"); // 視圖后綴return resolver;
}// 配置Thymeleaf視圖解析器
@Bean
public ViewResolver thymeleafViewResolver() {ThymeleafViewResolver resolver = new ThymeleafViewResolver();resolver.setTemplateEngine(templateEngine());resolver.setOrder(1); // 優先級高于JSPreturn resolver;
}// 配置Thymeleaf模板引擎
@Bean
public SpringTemplateEngine templateEngine() {SpringTemplateEngine engine = new SpringTemplateEngine();engine.setTemplateResolver(templateResolver());return engine;
}// 配置模板解析器
@Bean
public TemplateResolver templateResolver() {TemplateResolver resolver = new ServletContextTemplateResolver();resolver.setPrefix("/WEB-INF/templates/"); // 模板前綴resolver.setSuffix(".html"); // 模板后綴resolver.setTemplateMode("HTML5"); // 模板類型resolver.setCharacterEncoding("UTF-8"); // 編碼return resolver;
}
4.2 JSP模板集成與Thymeleaf模板配置
JSP視圖示例:
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title><%= request.getAttribute("title") %></title>
</head>
<body><h1><%= request.getAttribute("message") %></h1><p>Time: <%= request.getAttribute("time") %></p><div><ul><%List<User> userList = (List<User>) request.getAttribute("userList");for (User user : userList) {%><li><%= user.getName() %></li><%}%></ul></div>
</body>
</html>
Thymeleaf視圖示例:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title th:text="${title}">默認標題</title>
</head>
<body><h1 th:text="${message}">默認消息</h1><p>Time: <span th:text="${#dates.format(time, 'yyyy-MM-dd HH:mm:ss')}">2025-08-04 12:00:00</span></p><div th:each="user : ${userList}"><p th:text="${user.name}">用戶姓名</p></div><form th:action="@{/user/create}" method="post"><input type="text" name="name" th:value="${user.name}" required><input type="submit" value="提交"></form>
</body>
</html>
Thymeleaf與JSP路徑配置對比:
特性 | JSP | Thymeleaf |
---|---|---|
默認路徑 | /WEB-INF/views/ | /WEB-INF/templates/ ?或?/templates/ |
文件擴展名 | .jsp | .html |
編碼方式 | 需要手動處理 | 自動處理 |
前端兼容性 | 需要Servlet容器支持 | 無需Servlet容器支持,可直接在瀏覽器中預覽 |
安全性 | 需要手動處理XSS等安全問題 | 默認啟用上下文敏感的轉義功能,防止XSS |
4.3 模型數據傳遞(Model、ModelAndView)
使用Model傳遞數據:
@RestController
public class ModelController {@GetMapping("/model")public String modelExample(Model model) {model.addAttribute("message", "Hello Model!");model.addAttribute("time", new Date());return "modelExample";}@GetMapping("/model-and-view")public ModelAndView modelAndViewExample() {ModelAndView mav = new ModelAndView();mav.addObject("message", "HelloModelAndView!");mav.addObject("time", new Date());mav.setViewName("modelAndViewExample");return mav;}
}
使用Map傳遞數據:
@RestController
public class MapController {@GetMapping("/map")public String mapExample(@RequestParam String name, Map<String, Object> map) {map.put("greeting", "Hello, " + name + "!");map.put("time", new Date());return "mapExample";}
}
使用ModelAndView返回JSON數據 :
@RestController
public class JsonController {@GetMapping("/json")public String jsonExample() {return "{\"message\":\"Hello JSON!\"}";}@GetMapping("/json/model")public String jsonModelExample(Model model) {model.addAttribute("message", "Hello JSON Model!");return "jsonModelExample";}@GetMapping("/json/model-and-view")public ModelAndView jsonModelAndViewExample() {ModelAndView mav = newModelAndView();mav.addObject("message", "Hello JSONModelAndView!");mav.setViewName("jsonModelAndViewExample");return mav;}
}
第五章:表單處理與文件上傳
5.1 表單提交與數據綁定
表單提交處理:使用@RequestParam
或POJO接收表單數據:
@RestController
public class FormController {// 使用請求參數接收表單數據@PostMapping("/form/params")public String formParams(@RequestParam("name") String name,@RequestParam("age") int age,@RequestParam("email") String email) {return "提交成功: " + name + ", " + age + ", " + email;}// 使用POJO接收表單數據@PostMapping("/form/pojo")public String formPojo(@RequestBody User user) {return "提交成功: " + user.getName() + ", " + user.getAge() + ", " + user plemail();}
}
Thymeleaf表單綁定:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title th:text="${'創建用戶'}">創建用戶</title>
</head>
<body><form th:action="@{/user/create}" method="post" th:object="${user}"><div><label>姓名</label><input type="text" th:field="*{name}" required></div><div><label>年齡</label><input type="number" th:field="*{age}" required></div><div><label>郵箱</label><input type="email" th:field="*{email}" required></div><input type="submit" value="創建用戶"></form>
</body>
</html>
表單數據綁定說明:
th:object="${user}"
指定表單綁定的對象th:field="*{property}"
自動綁定對象的屬性到表單字段- 支持表單驗證和錯誤提示
5.2 文件上傳功能實現(CommonsMultipartResolver)
文件上傳控制器 :
@RestController
public class FileUploadController {// 單文件上傳@PostMapping("/uploadSingle")public String uploadSingleFile(@RequestParam("file") MultipartFile file) {try {// 保存文件到服務器String path = "/tmp/uploads/" + file.getOriginalFilename();file transferTo(new File(path));return "File上傳成功: " + file.getOriginalFilename();} catch (Exception e) {return "File上傳失敗: " + e.getMessage();}}// 多文件上傳@PostMapping("/uploadMultiple")public String uploadMultipleFiles(@RequestParam("files") List<MultipartFile> files) {try {for (MultipartFile file : files) {String path = "/tmp/uploads/" + file.getOriginalFilename();file transferTo(new File(path));}return "多文件上傳成功: " + files.size() + " files";} catch (Exception e) {return "多文件上傳失敗: " + e.getMessage();}}// 帶其他參數的文件上傳@PostMapping("/upload/with-params")public String uploadWithParams(@RequestParam("file") MultipartFile file,@RequestParam("username") String username) {try {// 保存文件到服務器String path = "/tmp/uploads/" + username + "-" + file.getOriginalFilename();file transferTo(new File(path));return "File上傳成功: " + file.getOriginalFilename();} catch (Exception e) {return "File上傳失敗: " + e.getMessage();}}
}
文件上傳注意事項:
- 需要添加
commons-fileupload
和commons-io
依賴 - 需要設置文件上傳的最大大小和內存限制
- 需要處理文件名重復和非法字符問題
- 建議使用相對路徑或配置文件指定上傳路徑
5.3 表單驗證與錯誤處理
表單驗證:使用@Valid
和JSR-303驗證注解 :
// 定義帶驗證的POJO
@Data
public class UserForm {@NotNull(message = "姓名不能為空")@Size(min = 2, max = 50, message = "姓名長度必須在2-50之間")private String name;@NotNull(message = "年齡不能為空")@Min(value = 0, message = "年齡不能為負數")@Max(value = 150, message = "年齡不能超過150")private Integer age;@NotNull(message = "郵箱不能為空")@Email(message = "郵箱格式不正確")private String email;
}// 控制器方法
@RestController
public class ValidationController {@PostMapping("/validate")public String validateUser(@Valid @RequestBody UserForm userForm, BindingResult bindingResult) {if (bindingResult.hasErrors()) {// 獲取所有錯誤信息List<String> errors = bindingResult AllErrors().stream().map田ObjectError::getDefaultMessage).collect(Collectors.toList());return "{\"status\":\"error\", \"messages\": " + errors + "}";}// 驗證通過,處理業務邏輯return "{\"status\":\"success\"}";}
}
前端驗證:使用JavaScript進行前端驗證 :
<script>
function validateForm() {const name = document.getElementById("name").value;const age = document.getElementById("age").value;const email = document.getElementById("email").value;if (name.length === 0) {alert("姓名不能為空");return false;}if (age < 0 || age > 150) {alert("年齡必須在0-150之間");return false;}if (!驗證郵箱格式(email)) {alert("郵箱格式不正確");return false;}return true;
}function validateEmail(email) {return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
</script>
第六章:RESTful API開發
6.1 RESTful URL設計(@PathVariable)
RESTful API設計原則:資源導向,使用HTTP方法表示操作 :
@RestController
@RequestMapping("/api/v1/employees")
public class EmployeeRestController {// 查詢所有員工@GetMappingpublic List<Employee> getEmployees() {// 從數據庫或服務獲取員工列表return employeeService的所有員工();}// 根據ID查詢員工@GetMapping("/employee/{id}")public Employee getEmployeeById(@PathVariable Long id) {Employee employee = employeeService.getEmployeeById(id);if (employee == null) {throw new EmployeeNotFoundException("員工ID不存在: " + id);}return employee;}// 創建員工@PostMappingpublic Employee createEmployee(@RequestBody Employee employee) {// 驗證員工信息validateEmployee(employee);// 調用服務層創建員工return employeeService.createEmployee(employee);}// 更新員工@PutMapping("/employee/{id}")public Employee updateEmployee(@PathVariable Long id,@RequestBody Employee employee) {// 驗證員工信息validateEmployee(employee);// 調用服務層更新員工Employee updatedEmployee = employeeService.updateEmployee(id, employee);if (updatedEmployee == null) {throw new EmployeeNotFoundException("員工ID不存在: " + id);}return updatedEmployee;}// 刪除員工@DeleteMapping("/employee/{id}")public void deleteEmployee(@PathVariable Long id) {if (!employeeService.deleteEmployee(id)) {throw new EmployeeNotFoundException("員工ID不存在: " + id);}}// 私有驗證方法private void validateEmployee(Employee employee) {// 驗證員工姓名if (employee.getName() == null || employee.getName().trim().length() == 0) {throw new IllegalArgumentException("員工姓名不能為空");}// 驗證員工郵箱if (employee plemail() != null && !驗證郵箱格式(employee plemail())) {throw new IllegalArgumentException("郵箱格式不正確");}}// 驗證郵箱格式private boolean validateEmail(String email) {return email != null && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);}
}
RESTful API設計最佳實踐:
- 使用名詞復數表示資源集合,如
/employees
- 使用HTTP方法表示操作,GET表示獲取,POST表示創建,PUT表示更新,DELETE表示刪除
- 使用路徑變量表示資源標識,如
/employees/{id)
- 使用查詢參數進行過濾和分頁,如
/employees?name=John&age=25
- 使用JSON格式進行數據交換
6.2 JSON數據處理(@RequestBody、@ResponseBody)
使用@RequestBody處理JSON請求 :
@RestController
public class JsonController {// 接收JSON請求體并轉換為Java對象@PostMapping("/json")public String jsonPost(@RequestBody User user) {// 處理用戶信息return "JSON數據接收成功: " + user plemail();}// 返回JSON響應@GetMapping("/json")public User jsonGet() {User user = new User();user.plemail("alice@example.com");user.name("Alice");user.age(25);return user;}// 處理嵌套JSON對象@PostMapping("/nestedjson")public String nestedJsonPost(@RequestBody UserWithAddress userWithAddress) {// 處理嵌套對象return "嵌套JSON接收成功: " + userWithAddress.user().name();}// 返回嵌套JSON對象@GetMapping("/nestedjson")public UserWithAddress nestedJsonGet() {User user = new User();user.name("Bob");user.age(30);Address address = new Address();address.city("北京");address.street("中關村大街");UserWithAddress userWithAddress = new UserWithAddress();userWithAddress.user(user);userWithAddress.address(address);return userWithAddress;}// 處理JSON數組@PostMapping("/jsonArrayPost")public String jsonArrayPost(@RequestBody List<User> userList) {// 處理用戶列表return "JSON數組接收成功: " + userList.size() + " users";}// 返回JSON數組@GetMapping("/jsonArrayGet")public List<User> jsonArrayGet() {// 從數據庫獲取用戶列表return Arrays.asList(new User("Alice", 25, "alice@example.com"),new User("Bob", 30, "bob@example.com"));}
}
JSON數據處理注意事項:
- 確保添加了
jackson-databind
依賴? - 復雜對象需要提供無參構造方法和getter/setter
- 可以使用
@JsonInclude
和@JsonProperty
注解控制JSON序列化/反序列化 - 建議使用
@RestController
代替@Controller
和@ResponseBody
組合
6.3 使用@RestController開發API
使用@RestController簡化API開發 :
@RestController
@RequestMapping("/api/v1/users")
public class UserController {// 查詢所有用戶@GetMappingpublic ResponseEntity<List<User>> allUsers() {List<User> userList = userService.allUsers();return ResponseEntity.ok(userList);}// 根據ID查詢用戶@GetMapping("/user/{id}")public ResponseEntity<User> userById(@PathVariable Long id) {User user = userService.userById(id);if (user == null) {return ResponseEntity notFound().build();}return ResponseEntity.ok(user);}// 創建用戶@PostMappingpublic ResponseEntity<User> createUser(@RequestBody User user) {// 驗證用戶信息validateUser(user);// 創建用戶User createdUser = userService.createUser(user);return ResponseEntity created().build();}// 更新用戶@PutMapping("/user/{id}")public ResponseEntity<User> updateUser(@PathVariable Long id,@RequestBody User user) {// 驗證用戶信息validateUser(user);// 更新用戶User updatedUser = userService.updateUser(id, user);if (updatedUser == null) {return ResponseEntity notFound().build();}return ResponseEntity.ok(updatedUser);}// 刪除用戶@DeleteMapping("/user/{id}")public ResponseEntity<?> deleteUser(@PathVariable Long id) {boolean deleted = userService.delete用戶(id);if (!deleted) {return ResponseEntity notFound().build();}return ResponseEntity noContent().build();}// 私有驗證方法private void validateUser(User user) {// 驗證用戶名if (user.name() == null || user.name().trim().length() == 0) {throw new IllegalArgumentException("用戶名不能為空");}// 驗證郵箱if (user.email() != null && !validateEmail(user.email())) {throw new IllegalArgumentException("郵箱格式不正確");}}// 驗證郵箱格式private boolean validateEmail(String email) {return email != null && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);}
}
使用@RestController的優勢:
- 自動將返回值轉換為JSON格式,無需添加
@ResponseBody
注解 - 更適合開發RESTful API,返回數據而非視圖
- 可以與
@ResponseStatus
、@ExceptionHandler
等注解結合使用
第七章:異常處理與攔截器配置
7.1 全局異常處理器(@ControllerAdvice)
全局異常處理器:使用@ControllerAdvice
處理整個應用的異常 :
@ControllerAdvice
public class GlobalExceptionHandler {// 處理Employee Not Found異常@ExceptionHandler(EmployeeNotFoundException.class)@ResponseBodypublic ResponseEntity<Error> handleEmployeeNotFound(EmployeeNotFoundException ex) {Error error = new Error();error.code(404);error.message(ex.getMessage());error.timestamp(new Date());return ResponseEntity notFound().body(error);}// 處理非法參數異常@ExceptionHandler(legalStateException.class)@ResponseBodypublic ResponseEntity<Error> handleIllegalState(IllegalArgumentException ex) {Error error = new Error();error.code(400);error.message(ex.getMessage());error.timestamp(new Date());return ResponseEntity badRequest().body(error);}// 處理其他異常@ExceptionHandler(Exception.class)@ResponseBodypublic ResponseEntity<Error> handleGeneralError(Exception ex) {Error error = new Error();error.code(500);error.message("服務器內部錯誤");error.timestamp(new Date());error.details(ex.getMessage());return ResponseEntity internal ServerError().body(error);}// 自定義錯誤對象@Datapublic class Error {private int code;private String message;private Date timestamp;private String details;}
}
異常處理最佳實踐:
- 使用
@ControllerAdvice
集中處理異常 - 不同異常類型返回不同的HTTP狀態碼
- 錯誤信息以JSON格式返回,包含狀態碼、消息、時間戳等信息
- 避免暴露敏感信息,如數據庫錯誤、堆棧跟蹤等
- 可以添加日志記錄,記錄異常詳細信息
7.2 自定義攔截器(HandlerInterceptor)
自定義攔截器:實現HandlerInterceptor
接口創建攔截器 :
public class LoginInterceptor implements HandlerInterceptor {// 在控制器方法執行前調用@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {// 獲取當前請求路徑String path = request.getRequestURI();// 忽略不需要登錄的路徑if (path.equals("/login") || path.equals("/css/**") || path.equals("/js/**")) {return true;}// 檢查用戶是否登錄User user = (User) request.getSession().getAttribute("LOGined_USER");if (user == null) {// 用戶未登錄,重定向到登錄頁面response.sendRedirect("/login");return false;}// 用戶已登錄,繼續執行return true;}// 在控制器方法執行后調用,視圖渲染前@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView mav) throws Exception {// 可以在此添加額外的處理}// 在整個請求處理完成后調用@Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {// 可以在此添加清理工作}
}
7.3 攔截器注冊與配置
在SpringMVC配置文件中注冊攔截器 :
<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"><!-- 開啟組件掃描 --><context:component-scan base-package="com.example"/><!-- 開啟注解驅動 --><mvc:annotation-driven /><!-- 配置視圖解析器 --><bean class="org.springframework.web.servlet.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/"/><property name="suffix" value=".jsp"/></bean><!-- 注冊攔截器 --><bean id="loginInterceptor" class="com.example.interceptor.LoginInterceptor"/><!-- 配置攔截器映射 --><mvc:interceptors><bean class="org.springframework.web.servlet.config annimation WebMvcConfigurationSupport"><property name="mapping" value="/**"/><property name="excludeMapping" value="/login, /css/**, /js/**"/></bean></mvc:interceptors>
</beans>
攔截器配置說明:
loginInterceptor
定義了攔截器的實現類mapping
指定需要攔截的路徑模式excludeMapping
指定不需要攔截的路徑- 攔截器按注冊順序執行preHandle方法,按相反順序執行postHandle和afterCompletion方法