一、简介

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 中的实现
ModelService 层 + Java Bean
ViewJSP、Thymeleaf、Freemarker 等
Controller控制层,工程中的servlet,@Controller / @RestController 注解的类;功能:接受请求,响应浏览器
DispatcherDispatcherServlet,是 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 的高级封装

对比ServletSpringMVC
入口每个 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:

传统 ServletSpringMVC 注解风格
需配置 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

比较维度SpringMVCServletStruts2
架构模式MVC非 MVC,结构混乱MVC
开发方式注解驱动,自动装配手动获取 request/response配置繁琐,OGNL 绑定
REST 支持✅ 原生支持❌ 不支持❌ 需要插件支持
参数绑定✅ 自动对象绑定 + 转换器❌ 手动处理✅ OGNL,但性能较差
异常处理✅ 注解 + 全局统一❌ 需 try-catch❌ 自定义 filter 实现
视图选择多视图支持JSPJSP / Freemarker
JSON 返回✅ 内置支持❌ 需写输出流✅ 支持 JSON 插件
社区活跃度✅ 非常高,主流标准❌ 已过时❌ 弃用趋势

2-3、RESTful 接口

RESTful 接口开发 = “资源 URL” + “HTTP 方法”,在 SpringMVC 中,就是用 @GetMapping@PostMapping@PutMapping@DeleteMapping 来准确表达“我要对这个资源做什么”。

举个比喻:

  • /users 是一群“用户”

  • /users/1 是用户 1

  • 然后你要“干什么”,就用 HTTP 方法说清楚

想干什么URL方法注解
所有用户/usersGET@GetMapping
一个用户/usersPOST@PostMapping
查询某个用户/users/1GET@GetMapping("/{id}")
修改某个用户/users/1PUT@PutMapping("/{id}")
删除某个用户/users/1DELETE

@DeleteMapping("/{id}")

三、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、常用属性详解:

属性名作用
valuepath请求的 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 中的一种通配符路径匹配规则,主要用于 @RequestMappingvalue 属性中,用来更灵活地匹配 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中获取请求参数

请求参数通常来自:

  1. URL 查询参数:例如 /login?username=Tom&password=123

  2. 表单提交(POST):例如 <form> 提交的数据

  3. 请求体(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根据浏览器语言返回不同语言的页面
认证AuthorizationToken 认证、JWT 登录
CORS 请求OriginReferer用于跨域校验或来源过滤

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 就可以自动封装:

  1. 请求参数名(form 中的 name)/URL参数 和 JavaBean 的属性名相同

  2. 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 会默认使用,只要参数名匹配!

它的解析顺序大致如下:

  1. 先从请求 URL 中找参数

  2. 再从表单字段(POST)中找参数

  3. 两边都有,就以表单字段为准(POST 参数优先)

  4. 找到的参数会通过 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、完整示例

功能目标

  1. 嵌套封装:学生有地址信息(Student 包含 Address

  2. List 集合封装:学生可以选多个兴趣爱好(List<String>

  3. 表单提交 → 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、设置请求对象编码

乱码问题分为两种:

  1. get请求的乱码;
  2. 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")
GETURL 地址栏设置 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 中的三种域对象

requestsession、和 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
}

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

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

相关文章

Java基础,反射破坏封装性 - 单例模式的崩塌

目录一、容易出现问题的小李代码小李的单例设计看似完美&#xff0c;实则存在三个致命问题&#xff1a;1、反射攻击的天然漏洞2、序列化的隐患3、性能瓶颈二、隔壁老王的优化方案三、为什么这样优化&#xff1f;四、小结周五下午&#xff0c;代码审查会议上&#xff0c;小李自信…

Neo4j 综合练习作业

Neo4j 综合练习作业 作业说明 这个作业涵盖了 Neo4j 的多个重要知识点&#xff0c;包括节点和关系的创建、查询、更新、删除以及高级查询功能。请使用 Cypher 语句完成以下所有题目。 数据准备 首先执行以下语句创建示例数据&#xff1a; ACTED_IN: 表示出演关系 DIRECTED: 表示…

基于PA算法的FTL引导

一、抽象绑定关系 1. 什么是 AF Block,什么是 NF Block,为什么要将多个 NF Block 绑定为一个 AF Block AF Block(Allocation Flash Block) 和 NF Block(NAND Flash Block) 是在 NAND Flash 存储架构中用于管理数据的基本单位。 AF Block 定义:AF Block 是一组多个 NF…

快速入门Java中的IO操作

以下是 Java 中常用的 IO 知识点总结&#xff1a; 1. 流的分类 按数据流向&#xff1a;输入流&#xff08;读取数据&#xff09;和输出流&#xff08;写入数据&#xff09;。按数据类型&#xff1a;字节流&#xff08;处理二进制数据&#xff0c;以字节为单位&#xff09;和字符…

小程序软装: 组件库开发

本节概述 经过前面小节的学习&#xff0c;我们已经搭建起了小程序的编译构建环境&#xff0c;能够将我们开发的小程序项目编译成为对应的逻辑代码文件 logic.js&#xff0c;页面渲染文件 view.js&#xff0c;样式文件 style.css 和配置文件 config.json 在编译小程序的过程中…

250708-Debian系统安装Edge浏览器并配置最小中文输入法

在 Debian 系统上安装 Microsoft Edge 浏览器可以通过以下几种方式进行。Microsoft 官方提供了 .deb 安装包&#xff0c;适用于 Debian、Ubuntu 及其衍生系统。 A. 如何安装&#xff1f; ✅ 方法一&#xff1a;使用 .deb 安装包&#xff08;推荐&#xff09; 步骤 1&#xff…

docker所占硬盘内存指令

使用下面命令可以查看docker所占的硬盘大小&#xff0c;如&#xff1a;docker system dfdocker system df -v

A1126LLHLX-T Allegro霍尔效应锁存器,5kHz+推挽输出,汽车级转速检测专家!

A1126LLHLX-T&#xff08;Allegro&#xff09;产品解析一、产品定位A1126LLHLX-T是Allegro MicroSystems推出的全极性霍尔效应锁存器&#xff0c;采用超薄SOT-23W封装&#xff08;1mm厚度&#xff09;&#xff0c;专为高可靠性位置检测与转速测量设计&#xff0c;具有低功耗、高…

【C#】File从后往前读取文件指定行数

/// <summary>/// 从后往前读取文件最后行数据/// </summary>/// <param name"filePath"></param>/// <param name"count"></param>/// <returns></returns>public static List<string> ReadFileRe…

暑假算法日记第五天

目标​&#xff1a;刷完灵神专题训练算法题单 阶段目标&#x1f4cc;&#xff1a;【算法题单】滑动窗口与双指针 LeetCode题目:683. K 个关闭的灯泡2067. 等计数子串的数量2524. 子数组的最大频率分数2269. 找到一个数字的 K 美丽值1984. 学生分数的最小差值1461. 检查一个字符…

【05】MFC入门到精通——MFC 为对话框中的控件添加变量 和 数据交换和检验

文章目录四、 为对话框中的控件添加变量五、对话框类的5.1 为编辑框添加变量面步骤中 为对话框添加了几个控件&#xff0c;包括三个静态文本框&#xff0c;三个编辑框&#xff0c;一个按钮控件。 四、 为对话框中的控件添加变量 编辑框中的数据可能会经常变化&#xff0c;有必…

4-Kafka-partition(分区)概念

Kafka Topic 分区详解 &#x1f4cc; 一、分区核心概念 1. 什么是分区&#xff1f; 物理分片&#xff1a;Topic 被划分为多个分区&#xff08;Partition&#xff09;&#xff0c;每个分区是一个有序、不可变的消息序列存储单位&#xff1a;每个分区对应一个物理日志文件&…

论文略读:UniPELT: A Unified Framework for Parameter-Efficient Language Model Tuning

ACL 2021 LoRAPrefix TuningAdapter门控蓝色参数是可训练的参数

【论文阅读】CogView: Mastering Text-to-Image Generation via Transformers

CogView&#xff1a;通过Transformers实现文本到图像的生成简介目标&#xff1a;通用领域中的文本到图像生成一直是一个开放的问题&#xff0c;它既需要强大的生成模型&#xff0c;也需要跨模态的理解。为了解决这个问题&#xff0c;我们提出了CogView&#xff0c;一个具有VQ -…

Typecho与WordPress技术架构深度对比:从LAMP到轻量级设计

文章目录 Typecho vs WordPress:深入比较两大博客系统的优劣与选型指南引言1. 系统概述与技术架构1.1 WordPress架构分析1.2 Typecho架构特点2. 核心功能对比2.1 内容管理能力2.2 主题与模板系统3. 性能与扩展性对比3.1 系统性能基准测试3.2 扩展生态系统4. 安全性与维护成本4…

CSS揭秘:8.连续的图像边框

前置知识&#xff1a;CSS 渐变&#xff0c;5. 条纹背景&#xff0c;border-image&#xff0c;基本的 CSS 动画前言 本文旨在实现图片边框效果&#xff0c;即在特定场景下让图片显示在边框而非背景区域。 一、传统实现方案 正常我们面对这样一个需求时&#xff0c;下意识会想到的…

Linux驱动学习day20(pinctrl子系统驱动大全)

一、Pinctrl作用Pinctrl(Pin Controller)&#xff1a;控制引脚引脚的枚举与命名、引脚复用、引脚配置。Pinctrl驱动一般由芯片原厂的BSP工程师来写&#xff0c;一般驱动工程师只需要在设备树中指明使用哪个引脚&#xff0c;复用为哪个功能、配置为哪些状态。二、Pin Controller…

Debiased All-in-one Image Restoration with Task Uncertainty Regularization

Abstract 一体化图像恢复是一项基础的底层视觉任务&#xff0c;在现实世界中有重要应用。主要挑战在于在单个模型中处理多种退化情况。虽然当前方法主要利用任务先验信息来指导恢复模型&#xff0c;但它们通常采用统一的多任务学习&#xff0c;忽略了不同退化任务在模型优化中的…

逆向 qq 音乐 sign,data, 解密 response 返回的 arraybuffer

解密 arraybuffer python requests 请求得到 arraybuffer&#xff0c;转为 hex 传递给 js res_data sign ctx.call("decrypt", response.content.hex())function decrypt(hex) {const bytes new Uint8Array(hex.length / 2);for (let i 0; i < hex.length; i …

PPT处理控件Aspose.Slides教程:在 C# 中将 ODP 转换为 PPTX

您是否正在寻找可靠的 PowerPoint SDK 来以编程方式开发ODP到PPTX转换器&#xff1f;本篇博文演示了如何使用 C# 将 ODP 转换为 PPTX。ODP是一种基于 XML 的演示文稿文件&#xff0c;可能包含图像、视频、文本等。但是&#xff0c;将打开的文档演示文稿转换为 PowerPoint 格式可…