Spring-MVC笔记
Spring MVC笔记
01 什么是Spring MVC
Spring MVC 是 Spring 框架 中的一个核心模块,专门用于构建 Web 应用程序 。它基于经典的 MVC 设计模式 (Model-View-Controller),但通过 Spring 的特性(如依赖注入、注解驱动)大幅简化了开发流程。
Spring MVC 是什么?
本质 :
一个基于 Java 的 Web 框架,帮助开发者快速、结构化地开发动态网站或 RESTful API。
核心思想 :
将应用程序拆分为 模型(Model)、视图(View)、控制器(Controller) 三个部分,实现职责分离,让代码更易维护和扩展。
Spring MVC 的作用
1. 处理用户请求和响应
• 用户通过浏览器发送请求(如点击链接、提交表单),Spring MVC 的控制器(Controller)接收请求,处理业务逻辑,最终返回响应(如 HTML 页面、JSON 数据)。
2. 解耦代码,分工协作
• Model(模型) :负责数据和业务逻辑(如数据库操作)。
• View(视图) :负责展示数据(如 HTML、JSP、Thymeleaf 模板)。
• Controller(控制器) :负责协调用户请求、调用模型、返回视图。
• 三者独立开发,修改某一层不会影响其他层。
3. 简化传统 Servlet 开发
• 传统 Servlet 需要手动处理 HTTP 请求参数、响应输出等底层细节,代码臃肿。
• Spring MVC 通过
注解(如
@RequestMapping
)
和
自动绑定
机制,让开发者专注业务逻辑。
• 例如,直接通过注解将请求参数绑定到 Java 对象:
@PostMapping("/user")
public String createUser(User user) { // 自动将表单参数封装到User对象
userService.save(user);
return "success";
}
4. 灵活适配多种技术
• 视图技术 :支持 JSP、Thymeleaf、FreeMarker 等模板引擎,甚至直接返回 JSON(适合前后端分离)。
•
数据交互
:轻松处理 JSON、XML 等数据格式(配合
@RestController
)。
• 整合其他框架 :无缝集成 Spring Security(安全)、Spring Data(数据库)等模块。
5. 强大的扩展性
• 通过拦截器(Interceptor)、全局异常处理(
@ControllerAdvice
)等机制,可以统一处理日志、权限、异常等问题。
• 例如,全局拦截未登录用户:
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (user未登录) {
response.sendRedirect("/login");
return false;
}
return true;
}
}
02 创建Spring MVC项目
以下是两种常用的 Spring MVC 项目搭建方式( 传统 XML 配置 和 Spring Boot 快速搭建 ):
一、传统方式:基于 Maven + XML 配置(适合学习底层原理)
1. 创建 Maven 项目
2. 添加依赖( pom.xml
)
<!-- Spring MVC 核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.29</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSP 支持 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
3. 配置 web.xml
(初始化 DispatcherServlet)
<web-app>
<!-- 配置前端控制器 DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 映射所有请求到 DispatcherServlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4. 创建 Spring MVC 配置文件( /WEB-INF/spring-mvc.xml
)
<!-- 开启注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器(JSP) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 扫描 Controller 包 -->
<context:component-scan base-package="com.example.controller"/>
5. 创建 Controller 和 JSP 视图
// com.example.controller.HomeController.java
@Controller
public class HomeController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello Spring MVC!");
return "hello"; // 对应 /WEB-INF/views/hello.jsp
}
}
<%-- /WEB-INF/views/hello.jsp --%>
<html>
<body>
<h1>${message}</h1>
</body>
</html>
6. 部署到 Tomcat
• 将项目打包为 WAR 文件,部署到 Tomcat 服务器,访问
http://localhost:8080/项目名/hello
。
二、快速方式:基于 Spring Boot
1. 使用 Spring Initializr 创建项目
• 访问 ,选择:
•
依赖
:
Spring Web
(已包含 Spring MVC)、
Thymeleaf
(模板引擎)。
• 打包方式 :JAR(内嵌 Tomcat,无需手动部署)。
2. 项目结构
src/
main/
java/
com.example.demo/
DemoApplication.java // 启动类
controller/
HomeController.java
resources/
templates/ // 存放视图(如HTML)
static/ // 存放静态资源(CSS/JS)
3. 编写 Controller
@Controller
public class HomeController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello Spring Boot MVC!");
return "hello"; // 对应 resources/templates/hello.html
}
}
4. 创建视图(Thymeleaf)
<!-- resources/templates/hello.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1 th:text="${message}"></h1>
</body>
</html>
5. 运行项目
• 直接运行
DemoApplication.java
的
main
方法,访问
http://localhost:8080/hello
。
三、关键配置说明
视图解析器 :
• 传统方式需手动配置 JSP 路径(如
/WEB-INF/views/*.jsp
)。• Spring Boot 默认使用
templates
目录存放视图(需配合模板引擎如 Thymeleaf)。静态资源访问 :
• Spring Boot 默认将
static
目录下的文件映射为静态资源(如http://localhost:8080/css/style.css
)。• 传统方式需在 XML 中配置
<mvc:resources location="/static/" mapping="/static/**"/>
。
四、常见问题
404 错误 :
• 检查 Controller 是否被扫描(
@ComponentScan
包路径是否正确)。• 视图文件是否放在正确目录(如
templates
或WEB-INF/views
)。依赖冲突 :
• 确保 Spring 版本与依赖库兼容(推荐使用 Spring Boot 自动管理版本)。
03 Spring MVC执行原理
Spring MVC 的执行原理基于
前端控制器模式
,其核心是
DispatcherServlet
,负责协调各组件处理请求。
1. 请求接收
• 用户发起请求 :客户端(浏览器)发送HTTP请求至Web应用。
•
前端控制器接管
:请求首先被
DispatcherServlet
(配置在
web.xml
中)拦截,作为统一入口处理所有请求。
2. 处理器映射(Handler Mapping)
•
查找处理器
:
DispatcherServlet
通过
HandlerMapping
根据请求URL(如
/hello
)找到对应的处理器(
Handler
),通常是
@Controller
中标注
@RequestMapping
的方法。
•
返回处理器链
:可能包含拦截器(
Interceptor
)和具体的控制器方法。
3. 处理器适配器(Handler Adapter)
•
执行处理器
:
HandlerAdapter
调用具体的处理器方法(如
@GetMapping
方法),处理不同形式的控制器(如基于注解或实现
Controller
接口)。
•
参数解析与绑定
:方法参数通过
ArgumentResolver
解析(如请求参数、模型、
@PathVariable
等)。
•
数据转换/验证
:使用
Converter
、
Validator
进行类型转换和数据校验。
4. 控制器处理
•
业务逻辑执行
:控制器方法调用Service层处理业务,返回结果(如
String
视图名、
ModelAndView
或
@ResponseBody
数据)。
•
模型数据填充
:将数据存储在
Model
对象中,供视图渲染使用。
5. 视图解析(View Resolution)
•
解析视图名
:
ViewResolver
根据控制器返回的视图名(如
"home"
)解析为具体的
View
对象(如JSP、Thymeleaf模板)。
•
示例配置
:
InternalResourceViewResolver
可能将
"home"
映射到
/WEB-INF/views/home.jsp
。
6. 视图渲染
•
模型数据传递
:
DispatcherServlet
将模型数据传递给
View
对象。
• 生成响应内容 :视图使用模板引擎(如JSP、Freemarker)渲染HTML,写入HTTP响应。
7. 返回响应
•
响应客户端
:渲染后的内容通过
DispatcherServlet
返回给客户端,完成请求-响应周期。
关键组件与扩展点
•
拦截器(Interceptor)
:在请求处理前后执行逻辑(如权限检查、日志记录),通过实现
HandlerInterceptor
接口配置。
•
异常处理
:
@ExceptionHandler
或
HandlerExceptionResolver
统一处理控制器抛出的异常。
•
文件上传
:
MultipartResolver
解析 multipart 请求(如文件上传)。
执行流程图示
客户端 → DispatcherServlet → HandlerMapping → HandlerAdapter → Controller → ModelAndView → ViewResolver → View → 响应
示例配置
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
// 视图解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
// 静态资源处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello Spring MVC!");
return "hello"; // 对应 /WEB-INF/views/hello.jsp
}
}
04 RequestMapping
在 Spring MVC 中,
@RequestMapping
是一个核心注解,用于将 HTTP 请求映射到特定的控制器方法。它提供了灵活的配置选项,支持定义请求路径、HTTP 方法、请求参数、请求头等条件。以下是其详细用法和功能说明:
1. 基本作用
• 定义请求映射规则 :将 URL 请求路径与控制器方法绑定。
• 支持多种请求类型 :GET、POST、PUT、DELETE 等。
• 支持路径变量、参数过滤、请求头过滤 等高级条件。
2. 可用位置
• 类级别 :定义共享的根路径(所有方法映射路径会继承该路径)。
• 方法级别 :定义具体方法的映射规则。
示例
@Controller
@RequestMapping("/user") // 类级别路径:所有方法映射路径以 /user 开头
public class UserController {
// 实际路径:/user/profile
@RequestMapping("/profile")
public String profile() { ... }
// 实际路径:/user/list
@RequestMapping("/list")
public String list() { ... }
}
3. 核心属性
(1) value
或 path
• 定义请求路径 :支持字符串或字符串数组(允许多路径映射)。
•
支持路径变量
(
{variable}
)和通配符(
*
,
**
)。
// 单路径
@RequestMapping("/detail")
// 多路径映射
@RequestMapping({"/info", "/detail"})
// 路径变量
@RequestMapping("/user/{id}")
public String getUser(@PathVariable Long id) { ... }
// 通配符匹配(如 /user/2023/order)
@RequestMapping("/user/*/order")
(2) method
•
指定允许的 HTTP 方法
:如
RequestMethod.GET
,
RequestMethod.POST
。
• 默认支持所有 HTTP 方法 。
// 只允许 GET 请求
@RequestMapping(value = "/list", method = RequestMethod.GET)
// 允许多个 HTTP 方法
@RequestMapping(value = "/save", method = {RequestMethod.POST, RequestMethod.PUT})
(3) params
• 过滤请求参数 :要求请求必须包含指定参数,或参数满足特定条件。
• 格式:
param
(必须存在)、
!param
(必须不存在)、
param=value
(值匹配)。
// 要求请求必须包含 id 参数
@RequestMapping(params = "id")
// 要求必须包含 id 且值为 100
@RequestMapping(params = "id=100")
// 要求不能包含 debug 参数
@RequestMapping(params = "!debug")
(4) headers
• 过滤请求头 :要求请求头满足特定条件。
• 格式与
params
类似。
// 要求请求头包含 Content-Type=application/json
@RequestMapping(headers = "Content-Type=application/json")
// 要求请求头必须包含 Auth-Token
@RequestMapping(headers = "Auth-Token")
(5) consumes
• 指定请求的 Content-Type :要求请求体的媒体类型匹配。
// 仅处理 Content-Type 为 application/json 的请求
@RequestMapping(consumes = "application/json")
(6) produces
• 指定响应的 Content-Type :设置响应体的媒体类型。
// 返回 JSON 数据
@RequestMapping(produces = "application/json")
4. 组合注解
为了简化代码,Spring 提供了基于
@RequestMapping
的快捷组合注解:
组合注解 | 等效写法 | 作用 |
---|---|---|
@GetMapping | @RequestMapping(method = RequestMethod.GET) | 处理 GET 请求 |
@PostMapping | @RequestMapping(method = RequestMethod.POST) | 处理 POST 请求 |
@PutMapping | @RequestMapping(method = RequestMethod.PUT) | 处理 PUT 请求 |
@DeleteMapping | @RequestMapping(method = RequestMethod.DELETE) | 处理 DELETE 请求 |
@PatchMapping | @RequestMapping(method = RequestMethod.PATCH) | 处理 PATCH 请求 |
示例
@GetMapping("/user/{id}") // 等价于 @RequestMapping(value="/user/{id}", method=RequestMethod.GET)
public String getUser(@PathVariable Long id) { ... }
5. 路径匹配规则
(1) 路径变量( {variable}
)
• 使用
@PathVariable
获取路径中的动态值。
// 匹配路径如 /user/123
@GetMapping("/user/{id}")
public String getUser(@PathVariable("id") Long userId) { ... }
(2) 通配符
•
?
:匹配单个字符(如
/user/2023?
匹配
/user/2023a
)。
•
*
:匹配同一层级的任意字符(如
/user/*/order
匹配
/user/123/order
)。
•
**
:匹配多层路径(如
/user/**
匹配
/user/123/order/456
)。
(3) 正则表达式
• 路径变量中可使用正则表达式限制格式。
// 限制 id 必须为数字
@GetMapping("/user/{id:\\d+}")
public String getUser(@PathVariable Long id) { ... }
6. 示例代码
(1) 完整控制器
@Controller
@RequestMapping("/product")
public class ProductController {
// 匹配 GET /product/detail?id=100
@GetMapping(value = "/detail", params = "id")
public String detail(@RequestParam Long id, Model model) {
model.addAttribute("product", productService.findById(id));
return "product/detail";
}
// 匹配 POST 或 PUT /product/save
@RequestMapping(value = "/save", method = {RequestMethod.POST, RequestMethod.PUT})
public String saveProduct(@ModelAttribute Product product) {
productService.save(product);
return "redirect:/product/list";
}
// 路径变量 + 正则匹配(如 /product/category/electronics)
@GetMapping("/category/{type:[a-z]+}")
public String listByCategory(@PathVariable String type, Model model) {
model.addAttribute("products", productService.findByCategory(type));
return "product/list";
}
}
05 Resful风格
RESTful( REpresentational State Transfer )是一种基于 HTTP 协议的 API 设计风格 ,强调以资源为中心,通过统一的接口和标准方法(GET/POST/PUT/DELETE 等)操作资源。
一、RESTful 的核心原则
资源(Resource)
• 所有数据或服务抽象为资源(如用户、订单),通过 URI(统一资源标识符) 唯一标识。
• 示例:
/users
(用户集合)、/users/1001
(ID 为 1001 的用户)。统一接口(Uniform Interface)
• 使用标准的 HTTP 方法 操作资源:
◦ GET :获取资源
◦ POST :创建资源
◦ PUT :更新资源(全量替换)
◦ PATCH :部分更新资源
◦ DELETE :删除资源
无状态(Stateless)
• 服务端不保存客户端状态,每个请求必须包含所有必要信息。
表述(Representation)
• 资源的表现形式(如 JSON、XML),客户端通过 HTTP 头(
Accept
/Content-Type
)协商格式。超媒体驱动(HATEOAS)
• 响应中包含相关资源的链接,客户端通过链接导航(可选约束,实际使用较少)。
二、RESTful API 设计规范
1. URI 设计规范
• 使用名词,而非动词 :URI 表示资源,动作由 HTTP 方法表达。
❌ 非 RESTful: /getUser?id=1001
✅ RESTful: GET /users/1001
•
层级关系使用
/
分隔
:
GET /users/1001/orders # 获取用户 1001 的所有订单
• 复数形式命名集合 :
GET /users # 用户集合
POST /users # 创建用户
• 过滤、排序、分页通过查询参数实现 :
GET /users?page=2&size=10&sort=name,asc # 分页排序
GET /users?name=John&age=30 # 过滤
2. HTTP 方法使用规范
HTTP 方法 | 操作类型 | 幂等性 | 示例 |
---|---|---|---|
GET | 查询资源 | 是 | GET /users/1001 |
POST | 创建资源 | 否 | POST /users |
PUT | 全量更新 | 是 | PUT /users/1001 |
PATCH | 部分更新 | 否 | PATCH /users/1001 |
DELETE | 删除资源 | 是 | DELETE /users/1001 |
3. 状态码(Status Code)
• 2xx :成功
•
200 OK
:常规成功
•
201 Created
:资源创建成功
•
204 No Content
:成功但无返回体(如删除操作)
• 4xx :客户端错误
•
400 Bad Request
:请求参数错误
•
401 Unauthorized
:未认证
•
403 Forbidden
:无权限
•
404 Not Found
:资源不存在
• 5xx :服务端错误
•
500 Internal Server Error
:服务器内部错误
4. 数据格式
• 使用 JSON 作为主流数据交换格式。
• 请求头指定
Content-Type: application/json
。
• 响应头包含
Content-Type: application/json
。
三、在 Spring 中实现 RESTful API
1. 使用 @RestController
注解
• 替代
@Controller
+
@ResponseBody
,直接返回 JSON 数据。
@RestController
@RequestMapping("/api/users")
public class UserController {
// ...
}
2. 映射 HTTP 方法
• 使用组合注解:
@GetMapping
,
@PostMapping
,
@PutMapping
,
@DeleteMapping
。
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) { ... }
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) { ... }
3. 处理请求和响应
•
路径变量
:
@PathVariable
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) { ... }
•
请求体
:
@RequestBody
@PostMapping
public User createUser(@RequestBody User user) { ... }
• 返回 ResponseEntity :自定义状态码和响应头
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
4. 统一异常处理
• 使用
@ExceptionHandler
和
@ControllerAdvice
返回标准错误响应。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException e) {
ErrorResponse error = new ErrorResponse(404, e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}
四、完整示例
1. 实体类
public class User {
private Long id;
private String name;
private String email;
// Getters and Setters
}
2. 控制器
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// 获取所有用户
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
// 获取单个用户
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
}
// 更新用户(全量)
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
return userService.update(user);
}
// 删除用户
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteUser(@PathVariable Long id) {
userService.deleteById(id);
}
}
3. 请求与响应示例
•
请求
:
POST /api/users
{
"name": "Alice",
"email": "alice@example.com"
}
• 响应 (状态码 201):
{
"id": 1001,
"name": "Alice",
"email": "alice@example.com"
}
06 重定向和转发
在 Spring MVC 中, 重定向(Redirect) 和 转发(Forward) 是两种不同的请求跳转方式,理解它们的区别和适用场景对开发至关重要。以下是详细对比及具体用法:
一、核心区别
特性 | 转发(Forward) | 重定向(Redirect) |
---|---|---|
发起方 | 服务端内部跳转(客户端无感知) | 服务端通知客户端重新发起请求 |
请求次数 | 1 次请求,1 次响应 | 2 次请求,2 次响应 |
地址栏变化 | 地址栏 URL 不变 | 地址栏 URL 变为目标地址 |
数据共享 | 共享同一 Request 作用域( request.setAttribute ) | 不共享 Request 作用域,需通过 URL 参数或 Session 传递 |
性能 | 更高(无额外请求) | 较低(多一次网络往返) |
使用场景 | 服务器内部资源跳转(如 JSP 间共享数据) | 跨应用跳转、防止表单重复提交(POST-REDIRECT-GET) |
二、在 Spring MVC 中的实现方式
1. 转发(Forward)
• 原理 :服务端内部将请求转发到另一个资源(如 JSP、控制器方法)。
•
语法
:在返回值前加
forward:
前缀。
• 示例 :
@GetMapping("/page1")
public String forwardDemo() {
// 转发到 /page2(客户端 URL 仍为 /page1)
return "forward:/page2";
}
@GetMapping("/page2")
public String page2() {
return "page2"; // 视图名对应视图解析器路径
}
•
数据传递
:通过
Model
或
HttpServletRequest
传递数据。
@GetMapping("/forward")
public String forward(Model model) {
model.addAttribute("message", "Hello from forward!");
return "forward:/targetPage";
}
2. 重定向(Redirect)
• 原理 :服务端返回 302 状态码和 Location 头,客户端自动发起新请求。
•
语法
:在返回值前加
redirect:
前缀。
• 示例 :
@PostMapping("/submit")
public String submitForm(FormData formData, RedirectAttributes redirectAttributes) {
// 处理表单数据...
// 重定向到 /result,传递参数
redirectAttributes.addAttribute("status", "success");
redirectAttributes.addFlashAttribute("message", "操作成功!");
return "redirect:/result";
}
@GetMapping("/result")
public String resultPage(@RequestParam String status, Model model) {
// 接收 URL 参数和 Flash 属性
return "result";
}
• 数据传递 :
•
URL 参数
:
RedirectAttributes.addAttribute("key", value)
→ 参数暴露在 URL 中。
•
Flash 属性
:
RedirectAttributes.addFlashAttribute("key", value)
→ 数据暂存 Session,一次请求后自动删除。
三、适用场景
1. 转发(Forward)
• 共享请求数据 :需要在多个视图或控制器间传递数据(如 JSP 到 JSP)。
• 隐藏实际资源路径 :保护内部资源路径,客户端无法直接访问。
• 统一预处理 :在转发前进行权限验证、日志记录等。
2. 重定向(Redirect)
• 防止表单重复提交 :提交后重定向到结果页(POST-REDIRECT-GET 模式)。
• 跨应用跳转 :跳转到外部网站或其他服务。
• 切换上下文路径 :如从 HTTP 跳转到 HTTPS,或更换域名。
五、完整代码示例
1. 转发示例
@Controller
public class ForwardController {
@GetMapping("/source")
public String sourcePage(Model model) {
model.addAttribute("data", "来自源页面的数据");
return "forward:/target"; // 转发到 /target
}
@GetMapping("/target")
public String targetPage(Model model) {
// 可以访问 model 中的 data
return "target-page"; // 视图模板路径
}
}
2. 重定向示例
@Controller
public class RedirectController {
@PostMapping("/save")
public String saveData(User user, RedirectAttributes redirectAttributes) {
userService.save(user);
// 添加 URL 参数(暴露在地址栏)
redirectAttributes.addAttribute("userId", user.getId());
// 添加 Flash 属性(安全传递敏感数据)
redirectAttributes.addFlashAttribute("message", "用户保存成功!");
return "redirect:/user/detail";
}
@GetMapping("/user/detail")
public String userDetail(@RequestParam Long userId, Model model) {
User user = userService.findById(userId);
model.addAttribute("user", user);
return "user-detail";
}
}
六、总结
• 转发(Forward) :适合服务器内部资源跳转,共享请求数据,性能更高。
• 重定向(Redirect) :适合客户端跳转、防止重复提交,需注意数据传递方式。
• 开发建议 :
• 表单提交后 必须使用重定向 避免重复提交。
• 优先使用
RedirectAttributes
传递数据,避免 URL 参数暴露敏感信息。
07 接收请求参数和数据回显
一、接收请求参数
1. 基本参数接收
•
@RequestParam
:获取 URL 参数或表单字段(默认必传,可设
required=false
)。
@GetMapping("/user")
public String getUser(@RequestParam("id") Long userId,
@RequestParam(value = "name", defaultValue = "Guest") String userName) {
// URL: /user?id=1001&name=Alice
// userId=1001, userName=Alice(若未传name,默认为"Guest")
return "user/detail";
}
•
@PathVariable
:获取 URL 路径变量。
@GetMapping("/user/{id}")
public String getUser(@PathVariable("id") Long userId) {
// URL: /user/1001 → userId=1001
return "user/detail";
}
2. 对象自动绑定
•
@ModelAttribute
:自动将表单字段绑定到对象(支持级联属性)。
@PostMapping("/save")
public String saveUser(@ModelAttribute User user) {
// 表单字段 name 和 email 自动绑定到 User 对象
userService.save(user);
return "redirect:/user/list";
}
• 无需注解 :直接声明对象参数,Spring 自动绑定。
@PostMapping("/save")
public String saveUser(User user) { // 效果同上
userService.save(user);
return "redirect:/user/list";
}
3. 接收 JSON 数据
•
@RequestBody
:将请求体中的 JSON 反序列化为对象。
@PostMapping("/api/user")
@ResponseBody
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.save(user);
return ResponseEntity.ok(savedUser);
}
4. 接收原生对象
• 直接使用
HttpServletRequest
、
HttpSession
等原生对象。
@GetMapping("/info")
public String getInfo(HttpServletRequest request, HttpSession session) {
String param = request.getParameter("param");
session.setAttribute("key", "value");
return "info";
}
二、数据回显(传递到视图)
1. 使用 Model
或 ModelMap
• 添加数据到模型,供视图(如 JSP、Thymeleaf)渲染。
@GetMapping("/user/edit")
public String editUser(@RequestParam Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user); // 回显到表单
return "user/edit";
}
2. 使用 ModelAndView
• 同时返回视图名和数据。
@GetMapping("/user/detail")
public ModelAndView userDetail(@RequestParam Long id) {
ModelAndView mav = new ModelAndView("user/detail");
User user = userService.findById(id);
mav.addObject("user", user);
return mav;
}
08 JSON
在 Spring 生态中, JSON(JavaScript Object Notation) 是主流的轻量级数据交换格式,而 Jackson 是处理 JSON 序列化与反序列化的核心库。
一、JSON 简介
1. 什么是 JSON?
•
轻量级数据格式
:以键值对(
key: value
)形式组织数据,易读且兼容性强。
• 数据结构 :
•
对象
:
{ "key": value }
•
数组
:
[ value1, value2 ]
•
值类型
:字符串、数字、布尔值、
null
、对象、数组。
2. 应用场景
• 前后端数据交互 :API 请求和响应。
•
配置文件
(如
package.json
)。
• NoSQL 数据库 (如 MongoDB)存储格式。
3. 对比 XML
特性 | JSON | XML |
---|---|---|
可读性 | 高(结构简洁) | 较低(标签冗余) |
数据体积 | 更小 | 更大 |
解析速度 | 更快 | 较慢 |
扩展性 | 弱(无命名空间、属性等概念) | 强(支持复杂结构) |
二、Jackson 核心功能
Jackson 是 Java 生态中最流行的 JSON 处理库 ,提供以下能力:
- 序列化 :将 Java 对象转换为 JSON 字符串。
- 反序列化 :将 JSON 字符串解析为 Java 对象。
- 数据绑定 :支持注解驱动配置。
- 流式 API :高性能处理大 JSON 数据。
三、Spring 中集成 Jackson
Spring MVC 默认通过
MappingJackson2HttpMessageConverter
集成 Jackson,自动处理 JSON 转换。
1. 添加依赖
<!-- Maven 依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
2. 启用 JSON 支持
•
注解驱动
:使用
@RestController
或
@ResponseBody
。
@RestController // 等效于 @Controller + @ResponseBody
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
四、Jackson 核心注解
通过注解控制序列化/反序列化行为:
1. 字段映射
•
@JsonProperty
:指定 JSON 字段名。
public class User {
@JsonProperty("user_name")
private String name;
}
// 序列化为 { "user_name": "Alice" }
•
@JsonIgnore
:忽略字段。
public class User {
@JsonIgnore
private String password;
}
2. 日期格式化
•
@JsonFormat
:自定义日期格式。
public class Order {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
}
// 序列化为 "2023-10-01 14:30:00"
3. 空值处理
•
@JsonInclude
:忽略空值字段。
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
private String email; // 若 email 为 null,不序列化
}
五、序列化与反序列化示例
1. Java 对象转 JSON(序列化)
ObjectMapper mapper = new ObjectMapper();
User user = new User("Alice", 30);
String json = mapper.writeValueAsString(user);
// 输出:{"name":"Alice","age":30}
2. JSON 转 Java 对象(反序列化)
String json = "{\"name\":\"Alice\",\"age\":30}";
User user = mapper.readValue(json, User.class);
六、Spring 中高级配置
1. 自定义 ObjectMapper
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper;
}
}
七、常见问题与解决方案
1. 日期格式不生效
• 问题 :日期字段未按预期格式序列化。
•
解决
:检查
@JsonFormat
的
timezone
配置或全局
ObjectMapper
日期格式。
2. 字段丢失
• 问题 :JSON 中缺少字段导致反序列化失败。
•
解决
:配置
ObjectMapper
忽略未知字段:
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
09 Fastjson
在 Java 开发中, Fastjson 是阿里巴巴开源的高性能 JSON 库,广泛用于序列化(Java 对象转 JSON)和反序列化(JSON 转 Java 对象)。与 Jackson 相比,Fastjson 在部分场景下性能更优,且 API 设计更简单易用。以下是其核心特性、使用方式及注意事项:
一、Fastjson 核心特性
高性能
• 序列化/反序列化速度极快,尤其在大数据量场景下表现优异。
• 依赖 ASM 字节码技术优化(无需反射),性能优于 Jackson 和 Gson。
功能丰富
• 支持复杂对象(泛型、嵌套对象、循环引用)。
• 支持 Java 8 时间 API(如
LocalDateTime
)。• 支持自定义序列化规则和过滤器。
简单 API
• 核心类
JSON
提供静态方法(如JSON.toJSONString()
、JSON.parseObject()
)。注解驱动
• 通过
@JSONField
注解灵活控制字段映射和格式。
二、与 Jackson 对比
特性 | Fastjson | Jackson |
---|---|---|
性能 | 更高(尤其序列化) | 较高 |
安全性 | 历史漏洞较多(需使用最新版本) | 安全性较好 |
API 简洁性 | 极简(静态方法) | 稍复杂(需 ObjectMapper 实例) |
社区生态 | 国内流行,文档丰富 | 国际主流,生态更成熟 |
维护状态 | 阿里巴巴维护,更新频繁 | 长期稳定更新 |
三、Spring 中集成 Fastjson
Spring Boot 默认使用 Jackson,若需替换为 Fastjson,需手动配置消息转换器。
1. 添加依赖
<!-- Maven 依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.34</version> <!-- 使用最新版本(避免安全漏洞) -->
</dependency>
2. 配置 Fastjson 消息转换器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 创建 Fastjson 消息转换器
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
// 配置序列化规则
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
SerializerFeature.PrettyFormat, // 格式化输出
SerializerFeature.WriteMapNullValue, // 输出空值字段
SerializerFeature.WriteDateUseDateFormat // 日期格式化
);
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
converter.setFastJsonConfig(config);
// 处理中文乱码
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(mediaTypes);
// 替换默认 Jackson 转换器
converters.add(0, converter);
}
}
四、核心 API 使用示例
1. 序列化(Java → JSON)
User user = new User("Alice", 25, LocalDateTime.now());
// 简单序列化
String json = JSON.toJSONString(user);
// 输出:{"age":25,"name":"Alice","createTime":"2023-10-01 14:30:00"}
// 带格式化的序列化
String prettyJson = JSON.toJSONString(user, SerializerFeature.PrettyFormat);
2. 反序列化(JSON → Java)
String json = "{\"name\":\"Bob\",\"age\":30}";
// 基本反序列化
User user = JSON.parseObject(json, User.class);
// 处理泛型(如 List)
String listJson = "[{\"name\":\"Alice\"}, {\"name\":\"Bob\"}]";
List<User> users = JSON.parseArray(listJson, User.class);
五、注解 @JSONField
通过注解控制字段的序列化行为:
1. 字段重命名
public class User {
@JSONField(name = "user_name")
private String name; // 序列化为 "user_name"
}
2. 忽略字段
public class User {
@JSONField(serialize = false)
private String password; // 不序列化
}
3. 日期格式化
public class User {
@JSONField(format = "yyyy-MM-dd")
private LocalDate birthDate;
}
4. 顺序控制
@JSONType(orders = {"id", "name", "age"}) // 类级别定义字段顺序
public class User {
private Long id;
private String name;
private Integer age;
}
10 ssm整合项目搭建
Spring MVC 图书管理系统开发流程与示例
一、项目目录结构(简化版)
src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── controller/ # 控制器层
│ │ │ └── BookController.java
│ │ ├── dao/ # DAO层(MyBatis Mapper接口)
│ │ │ └── BookMapper.java
│ │ ├── service/ # 业务层
│ │ │ ├── BookService.java
│ │ │ └── impl/
│ │ │ └── BookServiceImpl.java
│ │ └── entity/ # 实体类
│ │ └── Book.java
│ └── resources/
│ ├── mapper/ # MyBatis Mapper XML
│ │ └── BookMapper.xml
│ ├── spring/ # Spring配置
│ │ ├── spring.xml
│ │ └── spring-mvc.xml
│ └── jdbc.properties # 数据库配置
webapp/
├── WEB-INF/
│ └── views/ # JSP视图
│ └── book/
│ └── list.jsp
└── index.jsp
二、开发流程与代码示例
1. 创建数据库表
CREATE TABLE book (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
author VARCHAR(50),
price DECIMAL(10,2)
);
2. 实体类 Book.java
(POJO层)
package com.example.entity;
public class Book {
private Integer id;
private String name;
private String author;
private Double price;
// Getters and Setters
}
3. DAO层接口 BookMapper.java
package com.example.dao;
import com.example.entity.Book;
import java.util.List;
public interface BookMapper {
List<Book> selectAll();
int insert(Book book);
int deleteById(Integer id);
}
4. MyBatis Mapper XML BookMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.BookMapper">
<select id="selectAll" resultType="Book">
SELECT id, name, author, price FROM book
</select>
<insert id="insert" parameterType="Book">
INSERT INTO book(name, author, price)
VALUES(#{name}, #{author}, #{price})
</insert>
<delete id="deleteById" parameterType="Integer">
DELETE FROM book WHERE id = #{id}
</delete>
</mapper>
5. Service层实现业务逻辑
// 接口 BookService.java
package com.example.service;
import com.example.entity.Book;
import java.util.List;
public interface BookService {
List<Book> listBooks();
void addBook(Book book);
void deleteBook(Integer id);
}
// 实现类 BookServiceImpl.java
package com.example.service.impl;
import com.example.dao.BookMapper;
import com.example.entity.Book;
import com.example.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper bookMapper;
@Override
public List<Book> listBooks() {
return bookMapper.selectAll();
}
@Override
@Transactional
public void addBook(Book book) {
bookMapper.insert(book);
}
@Override
@Transactional
public void deleteBook(Integer id) {
bookMapper.deleteById(id);
}
}
6. Controller层处理请求
package com.example.controller;
import com.example.entity.Book;
import com.example.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/book")
public class BookController {
@Autowired
private BookService bookService;
// 查询所有书籍
@GetMapping("/list")
public String listBooks(Model model) {
model.addAttribute("books", bookService.listBooks());
return "book/list";
}
// 添加书籍(表单提交)
@PostMapping("/add")
public String addBook(Book book) {
bookService.addBook(book);
return "redirect:/book/list";
}
// 删除书籍
@GetMapping("/delete/{id}")
public String deleteBook(@PathVariable Integer id) {
bookService.deleteBook(id);
return "redirect:/book/list";
}
}
7. 配置文件
(1) jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/book_db?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
(2) spring.xml
(Spring核心配置)
<!-- 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- MyBatis SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- Mapper接口扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.dao"/>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.example.service"/>
(3) spring-mvc.xml
(Spring MVC配置)
<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 扫描控制器 -->
<context:component-scan base-package="com.example.controller"/>
<!-- 静态资源处理 -->
<mvc:resources mapping="/static/**" location="/static/"/>
8. 视图层 list.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>图书列表</title>
</head>
<body>
<h2>图书列表</h2>
<table border="1">
<tr>
<th>ID</th>
<th>书名</th>
<th>作者</th>
<th>价格</th>
<th>操作</th>
</tr>
<c:forEach items="${books}" var="book">
<tr>
<td>${book.id}</td>
<td>${book.name}</td>
<td>${book.author}</td>
<td>${book.price}</td>
<td>
<a href="/book/delete/${book.id}">删除</a>
</td>
</tr>
</c:forEach>
</table>
<h3>添加新书</h3>
<form action="/book/add" method="post">
书名:<input type="text" name="name"><br>
作者:<input type="text" name="author"><br>
价格:<input type="number" step="0.01" name="price"><br>
<button type="submit">提交</button>
</form>
</body>
</html>
三、关键流程说明
请求处理流程
浏览器请求 → DispatcherServlet → BookController → BookService → BookMapper → 数据库
分层职责
• DAO层 :直接操作数据库,提供
insert
、delete
等方法。• Service层 :处理业务逻辑(如事务管理
@Transactional
)。• Controller层 :接收HTTP请求,返回视图或重定向。
四、启动与测试
- 部署到Tomcat,访问
http://localhost:8080/book/list
。 - 添加图书:填写表单提交,数据插入数据库。
- 删除图书:点击删除链接,触发
DELETE
操作。
11 整合SSM框架的配置文件
SSM 整合核心配置文件详解
以下是整合 Spring、Spring MVC 和 MyBatis(SSM)时 必须配置的核心内容 ,分步骤说明每个配置文件的作用和关键配置项。
一、项目结构
src/
├── main/
│ ├── java/
│ │ └── com/example/
│ │ ├── controller/ # Spring MVC 控制器
│ │ ├── service/ # 业务层接口和实现
│ │ ├── dao/ # MyBatis Mapper 接口
│ │ └── entity/ # 数据库实体类
│ └── resources/
│ ├── spring/
│ │ ├── spring.xml # Spring 核心配置
│ │ └── spring-mvc.xml# Spring MVC 配置
│ ├── mapper/ # MyBatis Mapper XML 文件
│ └── jdbc.properties # 数据库连接配置
webapp/
├── WEB-INF/
│ └── views/ # JSP 视图文件
└── index.jsp
二、Spring 核心配置 ( spring.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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1. 加载数据库配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 2. 配置数据源(Druid) -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 连接池参数 -->
<property name="initialSize" value="5"/>
<property name="maxActive" value="20"/>
</bean>
<!-- 3. 配置 MyBatis 的 SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 绑定数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 指定 MyBatis Mapper XML 文件路径 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<!-- 可选:MyBatis 全局配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 4. 自动扫描 Mapper 接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.dao"/>
</bean>
<!-- 5. 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 6. 开启注解驱动的事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 7. 扫描 Service 层 -->
<context:component-scan base-package="com.example.service"/>
</beans>
关键配置说明 :
<context:property-placeholder>
加载
jdbc.properties
文件中的数据库连接参数。- •
jdbc.driver
- 数据库驱动类(如
com.mysql.cj.jdbc.Driver
)。 - •
jdbc.url
- 数据库连接 URL(如
jdbc:mysql://localhost:3306/test
)。 - •
jdbc.username
- /
jdbc.password
- 数据库用户名和密码。
DruidDataSource
• 使用 Druid 连接池管理数据库连接。
- •
initialSize
- /
maxActive
- 连接池初始大小和最大连接数。
SqlSessionFactoryBean
• 核心类,用于创建 MyBatis 的
SqlSession
。- •
mapperLocations
- 指定 Mapper XML 文件的路径(支持通配符
*
)。
MapperScannerConfigurer
• 自动扫描 DAO 层的 Mapper 接口,并注册为 Spring Bean。
- •
basePackage
- Mapper 接口所在的包路径。
DataSourceTransactionManager
• 基于数据源的事务管理器,用于管理数据库事务。
•
<tx:annotation-driven>
开启@Transactional
注解支持。
三、Spring MVC 配置 ( spring-mvc.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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 1. 开启注解驱动(支持 @Controller、@RequestMapping 等) -->
<mvc:annotation-driven/>
<!-- 2. 静态资源处理(CSS/JS/图片) -->
<mvc:resources mapping="/static/**" location="/static/"/>
<!-- 3. 视图解析器(JSP) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 4. 扫描 Controller 层 -->
<context:component-scan base-package="com.example.controller"/>
</beans>
关键配置说明 :
<mvc:annotation-driven>
• 启用 Spring MVC 的注解驱动功能(如
@Controller
、@RequestMapping
)。• 自动注册
RequestMappingHandlerMapping
和RequestMappingHandlerAdapter
。<mvc:resources>
• 处理静态资源请求(如
/static/css/style.css
)。- •
mapping
- URL 映射规则。
- •
location
- 静态资源在项目中的路径。
InternalResourceViewResolver
• 将逻辑视图名解析为具体的 JSP 文件路径。
- •
prefix
- 视图文件的前缀路径(如
/WEB-INF/views/book/list.jsp
)。 - •
suffix
- 视图文件的后缀(如
.jsp
)。
四、MyBatis 全局配置 ( mybatis-config.xml
,可选)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 1. 全局设置 -->
<settings>
<!-- 开启驼峰命名映射(数据库字段 user_name → Java 属性 userName) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 2. 实体类别名(简化 Mapper XML 中的类型书写) -->
<typeAliases>
<package name="com.example.entity"/>
</typeAliases>
</configuration>
关键配置说明 :
mapUnderscoreToCamelCase
• 自动将数据库字段的下划线命名转换为 Java 属性的驼峰命名。
typeAliases
• 为实体类设置别名,例如
<select resultType="Book">
代替全类名com.example.entity.Book
。
五、数据库连接配置 ( jdbc.properties
)
# MySQL 驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
# 数据库连接 URL(注意时区设置)
jdbc.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
注意事项 :
- •
serverTimezone=UTC
- 解决 MySQL 8.0 时区问题。
- •
useSSL=false
- 禁用 SSL 连接(生产环境不建议)。
六、常见问题与解决
- 报错
NoSuchBeanDefinitionException
- •
- 原因
- Spring 未扫描到 Service 或 Controller。
- •
- 解决
- 检查
<context:component-scan>
的base-package
路径。
- 静态资源无法加载
- •
- 原因
- 未配置
<mvc:resources>
或路径错误。 - •
- 解决
- 确认静态资源目录与配置一致。
- 事务不生效
- •
- 原因
- 未开启
<tx:annotation-driven>
或方法非public
。 - •
- 解决
- 确保
@Transactional
注解在public
方法上。
七、总结
• 最少配置项 :
- 数据源 (
dataSource
) - MyBatis 的
SqlSessionFactory
- Mapper 接口扫描 (
MapperScannerConfigurer
) - 事务管理器 (
DataSourceTransactionManager
) - Spring MVC 注解驱动 (
<mvc:annotation-driven>
) - 视图解析器 (
InternalResourceViewResolver
)
• 扩展建议 :
• 添加拦截器 (
HandlerInterceptor
) 实现权限控制。
• 使用
@RestController
开发 RESTful API。
• 集成 Swagger 生成 API 文档。
12 Spring MVC 拦截器
一、拦截器的作用
拦截器(Interceptor)是 Spring MVC 提供的一种 请求预处理和后处理机制 ,允许在 控制器方法执行前后 和 请求完成后的回调阶段 插入自定义逻辑。常用于以下场景:
• 权限验证 (如登录检查)
• 日志记录 (记录请求参数、执行时间)
• 性能监控 (统计接口耗时)
• 全局数据处理 (如设置请求/响应编码)
二、拦截器 vs. 过滤器
特性 | 拦截器(Interceptor) | 过滤器(Filter) |
---|---|---|
依赖框架 | Spring MVC 提供 | Servlet 规范提供 |
作用范围 | 仅拦截 Spring MVC 处理的请求 | 拦截所有请求(包括静态资源) |
访问对象 | 可获取 Handler(控制器方法)、ModelAndView | 只能获取 Servlet API(Request/Response) |
执行顺序 | 通过配置顺序控制 | 通过 @Order 或 web.xml 配置顺序 |
三、拦截器的核心方法
实现
HandlerInterceptor
接口,重写以下三个方法:
1. preHandle()
:在控制器方法执行前调用
• 返回值 :
•
true
:继续执行后续拦截器和控制器方法。
•
false
:中断请求,直接返回响应。
• 典型用途 :权限校验、请求参数预处理。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 示例:检查用户是否登录
if (request.getSession().getAttribute("user") == null) {
response.sendRedirect("/login");
return false;
}
return true;
}
2. postHandle()
:在控制器方法执行后、视图渲染前调用
•
可修改
ModelAndView
:调整返回的模型数据或视图。
• 典型用途 :向模型添加全局数据(如页面公共信息)。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
if (modelAndView != null) {
modelAndView.addObject("currentTime", System.currentTimeMillis());
}
}
3. afterCompletion()
:在请求完成后调用(视图渲染完毕)
• 资源清理 :关闭数据库连接、记录请求日志等。
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 示例:记录请求耗时
long startTime = (Long) request.getAttribute("startTime");
long cost = System.currentTimeMillis() - startTime;
log.info("请求 {} 耗时 {}ms", request.getRequestURI(), cost);
}
四、配置拦截器
在 Spring MVC 配置文件(如
spring-mvc.xml
)中注册拦截器,并指定拦截路径和排除路径。
1. 注册拦截器
<mvc:interceptors>
<!-- 全局拦截器(对所有请求生效) -->
<bean class="com.example.interceptor.LogInterceptor"/>
<!-- 自定义拦截器(指定路径) -->
<mvc:interceptor>
<mvc:mapping path="/admin/**"/> <!-- 拦截路径 -->
<mvc:exclude-mapping path="/admin/login"/> <!-- 排除路径 -->
<bean class="com.example.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
2. 路径匹配规则
•
/**
:匹配所有路径(包括子路径)。
•
/api/*
:匹配一级路径(如
/api/user
,不匹配
/api/user/1
)。
•
/static/**
:匹配静态资源路径。
五、示例:日志记录拦截器
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
request.setAttribute("startTime", System.currentTimeMillis());
log.info("请求开始: {} {}", request.getMethod(), request.getRequestURI());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
long startTime = (Long) request.getAttribute("startTime");
long cost = System.currentTimeMillis() - startTime;
log.info("请求结束: {} {} 耗时 {}ms", request.getMethod(), request.getRequestURI(), cost);
}
}
六、多拦截器的执行顺序
若有多个拦截器,执行顺序为:
preHandle()
:按配置顺序依次执行。postHandle()
:按配置的逆序执行。afterCompletion()
:按配置的逆序执行。
示例配置 :
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.example.interceptor.InterceptorA"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.example.interceptor.InterceptorB"/>
</mvc:interceptor>
</mvc:interceptors>
执行顺序 :
- InterceptorA.preHandle() → InterceptorB.preHandle()
- InterceptorB.postHandle() → InterceptorA.postHandle()
- InterceptorB.afterCompletion() → InterceptorA.afterCompletion()
七、常见问题
1. 拦截器不生效
• 原因 :未正确配置拦截路径或拦截器未被 Spring 管理。
•
解决
:检查
<mvc:mapping>
路径和包扫描配置。
2. 静态资源被拦截
•
原因
:拦截路径配置为
/**
且未排除静态资源。
•
解决
:使用
<mvc:exclude-mapping>
排除静态资源路径。
3. 修改响应后流程中断
•
解决
:在
preHandle()
中手动调用
response.getWriter().write()
并返回
false
。
八、总结
• 核心作用 :在请求生命周期的关键节点插入业务逻辑。
• 适用场景 :权限控制、日志记录、性能监控等。
• 配置要点 :路径匹配、执行顺序、排除规则。
13 文件上传和下载
一、文件上传
1. 添加依赖
在
pom.xml
中添加文件上传所需的依赖:
<!-- Apache Commons FileUpload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2. 配置 Spring MVC 文件解析器
在
spring-mvc.xml
中配置
MultipartResolver
:
<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 指定上传文件的最大大小(单位:字节) -->
<property name="maxUploadSize" value="10485760"/> <!-- 10MB -->
<!-- 指定默认编码 -->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
3. 编写上传 Controller
@Controller
@RequestMapping("/file")
public class FileController {
/**
* 文件上传处理
* @param file 前端传递的 MultipartFile 对象
* @param model 返回消息模型
* @return 上传结果页面
*/
@PostMapping("/upload")
public String uploadFile(
@RequestParam("file") MultipartFile file,
Model model
) {
try {
// 1. 检查文件是否为空
if (file.isEmpty()) {
model.addAttribute("msg", "上传文件不能为空!");
return "result";
}
// 2. 获取原始文件名并处理路径问题
String originalFilename = file.getOriginalFilename();
// 防止文件名中包含路径,只保留文件名
String fileName = new File(originalFilename).getName();
// 3. 指定文件存储路径(示例中存储在项目的 uploads 目录下)
String uploadDir = "uploads/";
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs(); // 创建目录
}
// 4. 生成唯一文件名(避免重复)
String savedFileName = UUID.randomUUID() + "_" + fileName;
File dest = new File(dir, savedFileName);
// 5. 保存文件到服务器
file.transferTo(dest);
model.addAttribute("msg", "文件上传成功!路径:" + dest.getAbsolutePath());
} catch (IOException e) {
model.addAttribute("msg", "文件上传失败:" + e.getMessage());
}
return "result";
}
}
4. 前端上传表单(JSP)
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<h2>文件上传</h2>
<!-- 必须指定 enctype="multipart/form-data" -->
<form action="/file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">上传</button>
</form>
</body>
</html>
二、文件下载
1. 编写下载 Controller
@Controller
@RequestMapping("/file")
public class FileController {
/**
* 文件下载处理
* @param filename 要下载的文件名
* @return 文件流响应
*/
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile(
@RequestParam("filename") String filename
) {
try {
// 1. 指定文件存储路径
String uploadDir = "uploads/";
File file = new File(uploadDir + filename);
// 2. 检查文件是否存在
if (!file.exists()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
// 3. 读取文件内容到字节数组
byte[] fileBytes = Files.readAllBytes(file.toPath());
// 4. 设置响应头
HttpHeaders headers = new HttpHeaders();
// 指定 Content-Disposition 告诉浏览器以下载方式处理
headers.add("Content-Disposition", "attachment;filename=" + filename);
// 设置 Content-Type(根据实际文件类型调整)
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
// 设置 Content-Length(可选)
headers.setContentLength(fileBytes.length);
// 5. 返回文件流
return new ResponseEntity<>(fileBytes, headers, HttpStatus.OK);
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
}
2. 前端下载链接(JSP)
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>文件下载</title>
</head>
<body>
<h2>文件下载</h2>
<!-- 示例:下载名为 example.txt 的文件 -->
<a href="/file/download?filename=example.txt">下载文件</a>
</body>
</html>
三、关键注解与配置说明
@RequestParam("file")
• 用于接收前端表单中
name="file"
的文件输入。MultipartFile
• Spring 提供的文件上传接口,提供
transferTo()
保存文件。ResponseEntity<byte[]>
• 封装 HTTP 响应的实体类,可直接返回字节数组和响应头。
HttpHeaders
• 设置响应头,如
Content-Disposition
(告诉浏览器下载文件)。
四、常见问题与解决
上传文件大小限制
• 修改
CommonsMultipartResolver
的maxUploadSize
属性。中文文件名乱码
• 确保
CommonsMultipartResolver
的defaultEncoding
设置为UTF-8
。文件存储路径权限问题
• 检查应用是否有权限写入目标目录。
安全建议
• 使用
UUID
重命名文件,避免文件名冲突和路径遍历攻击。
五、完整流程示意图
客户端 → 上传表单 → Spring MVC → MultipartResolver 解析 → Controller 保存文件
客户端 → 下载链接 → Controller 读取文件 → 返回字节流 → 浏览器下载