SpringMVC实战指南:从环境搭建到功能实现全解析

第一章: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方法使用场景
@GetMappingGET获取资源信息
@PostMappingPOST创建新资源
@PutMappingPUT更新资源
@DeleteMappingDELETE删除资源
@PatchMappingPATCH部分更新资源
@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;}
}

集合参数绑定:使用ListSet接收参数:

@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-fileuploadcommons-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路径配置对比

特性JSPThymeleaf
默认路径/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-fileuploadcommons-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设计最佳实践

  1. 使用名词复数表示资源集合,如/employees
  2. 使用HTTP方法表示操作,GET表示获取,POST表示创建,PUT表示更新,DELETE表示删除
  3. 使用路径变量表示资源标识,如/employees/{id)
  4. 使用查询参数进行过滤和分页,如/employees?name=John&age=25
  5. 使用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;}
}

异常处理最佳实践

  1. 使用@ControllerAdvice集中处理异常
  2. 不同异常类型返回不同的HTTP状态码
  3. 错误信息以JSON格式返回,包含状态码、消息、时间戳等信息
  4. 避免暴露敏感信息,如数据库错误、堆栈跟踪等
  5. 可以添加日志记录,记录异常详细信息
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方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91817.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Repo 与 manifest

Manifest&#xff1a;它本身就是一个 git 仓库&#xff0c;其中存放的都是包含仓库和子仓库信息的XML文件。这些文件全部由开发者或者维护者手动配置并自己上传到 git 仓库。另外&#xff1a;Manifest 中的仓库之间的依赖关系 repo 也并不关心。所以它们可以是同级的也可以是包…

深入浅出 RabbitMQ:简单队列实战指南

大家好&#xff0c;我是工藤学编程 &#x1f989;一个正在努力学习的小博主&#xff0c;期待你的关注实战代码系列最新文章&#x1f609;C实现图书管理系统&#xff08;Qt C GUI界面版&#xff09;SpringBoot实战系列&#x1f437;【SpringBoot实战系列】SpringBoot3.X 整合 Mi…

Ubuntu22-Qt Creator-fcitx-中文输入

fcitx在ubuntu系统中路径 /usr/lib/x86_64-linux-gnu/qt5/plugins/platforminputcontexts/ /usr/lib/x86_64-linux-gnu/qt6/plugins/platforminputcontexts/ fcitx-qt5-1.2.7 编译 下载链接:https://github.com/fcitx/fcitx-qt5/archive/refs/tags/1.2.7.zip Qt版本:Qt C…

【Java基础|第十三篇】面向对象基础(三)——继承(一)继承的理解,实现,特点……

&#xff08;四&#xff09;面向对象&#xff1a; 5、继承&#xff1a; &#xff08;1&#xff09;理解&#xff1a; 概念&#xff1a; 继承是面向对象的三大特征之一 继承是类与类之间关系的一种&#xff08;是父类与子类的关系&#xff09; 使用场景&#xff1a; 一个类与另…

QGIS绿色版吉林一号切片体验版插件(Jilin1Tiles)更新

吉林一号更新2024年图源了但吉林一号切片体验版插件&#xff08;Jilin1Tiles&#xff09;还没有更新&#xff0c;我修改了一下代码&#xff0c;直接集成到QGIS绿色版中。如下&#xff1a;注意&#xff1a;第一次使用的时候需要选中启用一下插件&#xff1a;需要使用的可以直接下…

git操作命令和golang编译脚本

git子模块信息处理命令git init submodule git submodule updategit取消合并 git merge --abort git reset --hard HEAD{1}bat文件生成二进制set GOOSlinux set GOARCHamd64 go env -w GOFLAGS-modvendor go build -ldflags "-w -s" -ohallapiset GOOSlinux set GOAR…

通往L4之路:构建自我进化的智能驾驶决策大脑

摘要&#xff1a; 本文旨在提出一个超越当前主流“感知-预测-规划”分离式架构的下一代自动驾驶决策系统方案。面对自动驾驶领域最核心的“长尾场景”难题&#xff0c;本文借鉴并升华了一套源于复杂策略制定的决策智能框架&#xff0c;通过构建动态驾驶世界模型&#xff08;Dyn…

AI编程助手:终结996的新希望

引言程序员工作现状与“996”现象的普遍性AI技术快速发展对编程效率的潜在影响核心问题&#xff1a;AI IDE与AI辅助编程能否改变传统开发模式AI IDE与AI辅助编程的核心技术AI IDE的定义与功能&#xff08;代码补全、错误检测、自动重构等&#xff09;AI辅助编程工具&#xff08…

Anthropic 禁止 OpenAI 访问 Claude API:商业竞争与行业规范的冲突

Anthropic 禁止 OpenAI 访问 Claude API&#xff1a;商业竞争与行业规范的冲突 文章来源&#xff1a;Poixe AI 本周&#xff0c;美国 AI 公司 Anthropic 宣布禁止 OpenAI 通过 API 访问其 Claude 系列大模型。这一举动引发了行业对"友好基准测试"与商业竞争边界的热…

区块链 + 物联网落地案例:供应链溯源系统开发全记录

本文详细记录了区块链与物联网技术融合的供应链溯源系统开发全流程。从项目背景出发&#xff0c;阐述传统供应链溯源痛点&#xff0c;介绍系统开发的技术架构设计&#xff0c;包括物联网数据采集层、区块链数据存储层等核心模块&#xff0c;详解硬件选型、智能合约编写、数据上…

Windows环境下Intel Fortran如何安装配置NetCDF

NetCDF(Network Common Data Form)格式,简称nc格式,是一种自描述、与平台无关的二进制数据文件,特别适合多维数据的存储和交换,广泛应用于气象、海洋、地球科学等领域。本文介绍Windows环境下IntelFortran安装配置NetCDF的过程。 一、系统环境及准备工作 1. 系统 Wind…

tcp/udp的socket特点

tcp &#xff1a; 绑定一个 socket 只是用来监听&#xff0c;accept 对每个客户端生成一个 socket 用来维护滑动窗口等。每个客户端用一个 socket 用来维护滑动窗口等。 4 次挥手对应两次 close 的 fin 和返回的 ack。 而三次挥手在 connect 里阻塞完成。 ​udp &#xff1a; 双…

Linux命令top

top一、 命令二、 如何查看top输出的结果一、 命令 top命令是Linux中的一个实时进程监控工具&#xff0c;类似于windows中的任务管理器。 基本命令 top二、 如何查看top输出的结果 我们需要分析top输出的结果 top输出的结果分为上下两部分&#xff0c;先看上半部分 第一行是…

Perl 数据库连接

Perl 数据库连接 概述 Perl是一种强大的编程语言&#xff0c;广泛应用于文本处理、系统管理、网络编程等领域。随着数据库技术的快速发展&#xff0c;Perl与数据库的结合也日益紧密。本文将详细介绍Perl数据库连接的相关知识&#xff0c;包括常用的数据库类型、连接方法以及一些…

jenkins从入门到精通-P1—九五小庞

1. jenkins的两个核心为CI持续集成 CD持续部署2.jenkins在企业工作中的流程3. 学习的内容包括

第九节 Redis 事务、Redis 脚本

Redis 事务可以一次执行多个命令&#xff0c; 并且带有以下两个重要的保证&#xff1a; 事务是一个单独的隔离操作&#xff1a;事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中&#xff0c;不会被其他客户端发送来的命令请求所打断。事务是一个原子操作&#x…

托福阅读37-2

托福阅读37-2 1.reinforcement from reintroduction定位到倒数第二句&#xff0c;这里我没看懂former和term&#xff0c;直接懵掉了&#xff0c;然后往后看。这句话其实省略了&#xff0c;补充完应该是The former is termed reintroduction and the latter is termed reinforce…

docker-compose一键部署Springboot+Vue前后端分离项目

1. 背景说明 后端使用JDK8&#xff0c;前端为普通Vue项目前端访问后端接口&#xff0c;统一带了前缀/api 2. 项目配置 2.1 后端 yml文件里配置统一访问前缀/api2.2 前端 API路径配置为相对路径&#xff1a;说明&#xff1a;我这边前后端应用都是部署在同一台服务器上&#xff0…

Unity_数据持久化_XML基础

Unity数据持久化 三、XML数据持久化 3.1 XML基础概念 3.1.1 什么是XML XML&#xff08;eXtensible Markup Language&#xff09;**是一种可扩展的标记语言&#xff0c;用于存储和传输数据。它具有以下特点&#xff1a; 结构化&#xff1a;数据以层次结构组织可读性&#xff1a;…

大语言模型的解码策略:贪婪解码与波束搜索

在自然语言生成任务&#xff08;如机器翻译、文本摘要、图像描述等&#xff09;中&#xff0c;语言模型通常会输出一个词的概率分布&#xff0c;但模型本身并不会自动告诉你应该选哪些词作为最终的输出句子。因此&#xff0c;我们需要设计一个“解码策略”来从这些概率中生成可…