SpringMVC 入门核心笔记

1、学习方法说明

一、SpringMVC 学习核心方法论

SpringMVC 是 Spring 生态的 Web 层框架,学习需遵循「原理先行、实战落地、循序渐进」的原则,核心方法如下:

先懂原理,再写代码

  • 先吃透 MVC 架构、Servlet 基础、SpringMVC 执行流程,再动手写代码,避免只会复制粘贴。
  • 重点理解「请求 - 响应」的完整链路,每个组件的作用和分工。

从传统 XML 到注解,再到 SpringBoot

  • 先学传统 XML 配置,理解底层原理;再学注解开发,提升效率;最后过渡到 SpringBoot 自动配置,符合技术演进路线。

结合 SSM 全栈学习

  • SpringMVC 是 Web 层,需结合 Spring(业务层)、MyBatis(持久层)一起学习,理解完整的 SSM 架构流转。

多调试、多踩坑、多总结

  • 遇到 404、500 等错误,通过日志定位问题,总结常见坑(如路径配置、参数绑定、乱码问题)。

源码辅助理解

  • 核心组件(如 DispatcherServlet)的源码不用全背,但要理解核心执行流程,知其然更知其所以然。

2、回顾 MVC 架构

一、MVC 核心定义

MVC 是一种软件架构设计模式 ,将应用程序分为 3 个核心层级,实现「关注点分离」,降低耦合度,提升可维护性。

二、MVC 三层核心角色

层级 全称 核心作用 对应技术
Model(模型层) Model 封装业务数据和业务逻辑,处理数据的增删改查 Service、Dao、Entity
View(视图层) View 负责数据展示,与用户交互,不处理业务逻辑 JSP、Thymeleaf、Vue
Controller(控制层) Controller 接收用户请求,调用 Model 处理业务,将结果返回给 View SpringMVC Controller

三、MVC 架构执行流程

  1. 用户发送请求 → Controller 接收请求
  2. Controller 调用 Model 处理业务逻辑、操作数据
  3. Model 处理完成后,将结果返回给 Controller
  4. Controller 将结果传递给 View,渲染页面
  5. View 将最终页面响应给用户

四、MVC 架构优势

解耦 :三层职责分离,修改某一层不影响其他层,符合开闭原则✅ 可复用 :Model 可被多个 View 复用,避免代码冗余✅ 可维护性高 :分层清晰,便于团队协作开发和后期维护✅ 适合 Web 开发:完美适配 Web 应用的「请求 - 响应」模型


3、回顾 Servlet

一、Servlet 核心定义

Servlet 是 Java EE 规范的核心组件,是运行在 Web 服务器(如 Tomcat)上的 Java 程序,用于处理 Web 请求、生成动态响应,是 SpringMVC 的底层基础。

二、Servlet 核心生命周期

阶段 执行时机 核心方法 作用
实例化 第一次请求 / 服务器启动时 构造方法 创建 Servlet 实例
初始化 实例化后,仅执行 1 次 init() 初始化资源(如数据库连接)
服务 每次请求时,多次执行 service() 处理请求,根据请求方法调用 doGet()/doPost()
销毁 服务器关闭 / 项目卸载时 destroy() 释放资源

三、Servlet 核心问题(传统开发痛点)

传统 Servlet 开发存在以下问题,催生了 SpringMVC:

  • 代码冗余:每个请求都需要写一个 Servlet,类爆炸问题严重
  • 配置繁琐 :需要在 web.xml 中配置 Servlet 映射,修改需重启服务器
  • 参数处理麻烦:手动获取请求参数、类型转换、乱码处理
  • 页面跳转繁琐:手动编写转发 / 重定向代码
  • 与业务层耦合:Servlet 中直接调用 Service,耦合度高

四、SpringMVC 与 Servlet 的关系

SpringMVC 是基于 Servlet 的封装框架 ,核心入口 DispatcherServlet 就是一个 Servlet,它统一处理所有请求,再分发到对应的 Controller,解决了传统 Servlet 的所有痛点。


4、初识 SpringMVC

一、SpringMVC 核心定义

SpringMVC 是 Spring 生态的 Web 层框架,是 Spring 对 Servlet 的封装,实现了 MVC 架构,用于处理 Web 请求、响应,是 Java 后端 Web 开发的主流框架。

二、SpringMVC 核心优势

原生继承 Spring:无缝整合 Spring IOC/AOP/ 事务,无需额外配置

MVC 架构清晰:分层明确,职责分离,开发效率高

功能强大:支持参数绑定、文件上传、拦截器、异常处理、RESTful 等

配置灵活:支持 XML / 注解 / JavaConfig 多种配置方式

生态完善:完美适配 SSM 架构,是 SpringBoot Web 开发的底层基础

三、第一个 SpringMVC 程序(环境搭建)

1. 导入 Maven 依赖

xml

复制代码
<dependencies>
    <!-- SpringMVC 核心依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- Servlet API(provided,Tomcat 自带) -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- JSP API(provided) -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
2. 配置 web.xml(核心入口)

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 1. 配置 DispatcherServlet(SpringMVC 核心入口) -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 加载 SpringMVC 配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!-- 启动时加载,优先级1 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 2. 配置 Servlet 映射,拦截所有请求 -->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 3. 配置乱码过滤器(POST请求乱码) -->
    <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>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>
3. 配置 springmvc-servlet.xml

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1. 开启组件扫描,扫描 Controller -->
    <context:component-scan base-package="com.example.controller"/>

    <!-- 2. 配置视图解析器(InternalResourceViewResolver) -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀:JSP 所在目录 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀:JSP 扩展名 -->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>
4. 编写 Controller
复制代码
package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

// 标记为 Controller,注册为 Bean
@Controller
public class HelloController {

    // 映射请求路径:/hello
    @RequestMapping("/hello")
    public String hello() {
        // 返回视图名,视图解析器会拼接为 /WEB-INF/jsp/hello.jsp
        return "hello";
    }
}
5. 编写 JSP 视图

WEB-INF/jsp/ 下创建 hello.jsp

jsp

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello SpringMVC</title>
</head>
<body>
    <h1>Hello, SpringMVC!</h1>
</body>
</html>
6. 测试

启动 Tomcat,访问 http://localhost:8080/hello,即可看到页面,第一个 SpringMVC 程序运行成功!


5、SpringMVC 执行原理(面试必考)

一、核心执行流程(完整 11 步)

SpringMVC 的核心是 DispatcherServlet(前端控制器),所有请求都由它统一分发,执行流程如下:

  1. 用户发送请求DispatcherServlet 拦截所有请求,作为入口
  2. DispatcherServlet 调用 HandlerMapping(处理器映射器)
  3. HandlerMapping 根据请求路径,找到对应的 Handler(Controller) 和拦截器,返回 HandlerExecutionChain
  4. DispatcherServlet 调用 HandlerAdapter(处理器适配器)
  5. HandlerAdapter 适配 Handler,执行对应的 Controller 方法
  6. Controller 方法执行完成,返回 ModelAndView(模型数据 + 视图名)
  7. HandlerAdapterModelAndView 返回给 DispatcherServlet
  8. DispatcherServlet 调用 ViewResolver(视图解析器),根据视图名解析出真实视图(如 JSP)
  9. ViewResolver 返回视图对象给 DispatcherServlet
  10. DispatcherServlet 调用视图的 render() 方法,渲染模型数据到视图
  11. 视图响应给用户,完成请求

二、核心组件详解

组件 全称 核心作用
DispatcherServlet 前端控制器 核心入口,统一处理所有请求,分发请求、协调各组件
HandlerMapping 处理器映射器 根据请求路径,找到对应的 Controller
HandlerAdapter 处理器适配器 适配 Controller,执行 Controller 方法
Handler 处理器 即 Controller,处理业务逻辑,返回 ModelAndView
ViewResolver 视图解析器 根据视图名,解析出真实视图(如 JSP、Thymeleaf)
View 视图 渲染模型数据,响应给用户

三、执行流程极简图

复制代码
用户请求 → DispatcherServlet → HandlerMapping → Handler(Controller)
          ↓
          HandlerAdapter → ModelAndView → ViewResolver → View → 用户响应

6、深入 SpringMVC 学习

一、核心知识点深入

1. 请求参数绑定

SpringMVC 自动将请求参数绑定到 Controller 方法的形参,支持多种类型:

  • 基本数据类型、字符串
  • 实体类(自动封装,属性名与请求参数名一致)
  • 数组、集合
  • @RequestParam:指定参数名,设置必填 / 默认值
  • @PathVariable:RESTful 路径参数绑定
2. 常用注解
  • @RequestMapping:映射请求路径,支持方法、类
  • @GetMapping/@PostMapping:指定请求方法(GET/POST)
  • @RequestParam:获取请求参数
  • @PathVariable:获取路径参数
  • @RequestHeader:获取请求头
  • @CookieValue:获取 Cookie 值
  • @ModelAttribute:数据回显、预处理
3. 视图与跳转
  • 转发return "视图名"(默认转发,地址栏不变)
  • 重定向return "redirect:/路径"(地址栏变化,两次请求)
  • 无视图返回@ResponseBody 直接返回 JSON 数据(前后端分离)
4. 拦截器(Interceptor)
  • 类似 Servlet 过滤器,用于拦截请求,做权限校验、日志、统一处理
  • 实现 HandlerInterceptor 接口,重写 preHandle()/postHandle()/afterCompletion() 方法
  • 可配置多个拦截器,按顺序执行
5. 异常处理
  • 全局异常处理:@ControllerAdvice + @ExceptionHandler
  • 统一处理所有 Controller 的异常,返回友好页面 / JSON
6. 文件上传 / 下载
  • 支持单文件 / 多文件上传,通过 MultipartFile 接收
  • 配置文件上传解析器 CommonsMultipartResolver

7、使用注解开发 SpringMVC

一、核心注解(零 XML 开发)

SpringMVC 支持纯注解开发,完全替代 XML 配置,核心注解如下:

注解 作用
@Controller 标记类为 Controller,注册为 Bean
@RequestMapping 映射请求路径,支持类 + 方法
@GetMapping/@PostMapping 映射 GET/POST 请求
@ResponseBody 直接返回 JSON 数据,不跳转视图
@RestController @Controller + @ResponseBody 组合,用于前后端分离
@RequestParam 获取请求参数,指定参数名
@PathVariable 获取 RESTful 路径参数
@ComponentScan 开启组件扫描,替代 XML 扫描配置
@Configuration 标记为配置类,替代 XML 配置文件
@EnableWebMvc 开启 SpringMVC 注解支持,替代 XML 配置

二、纯注解开发示例

1. 配置类(替代 springmvc-servlet.xml)
复制代码
@Configuration
@ComponentScan("com.example")
@EnableWebMvc // 开启 SpringMVC 注解支持
public class SpringMVCConfig implements WebMvcConfigurer {

    // 配置视图解析器
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    // 配置乱码过滤器(替代 web.xml 配置)
    @Bean
    public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
        FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>();
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        filter.setForceEncoding(true);
        bean.setFilter(filter);
        bean.addUrlPatterns("/*");
        return bean;
    }
}
2. 初始化类(替代 web.xml)
复制代码
public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    // 加载 Spring 容器配置(业务层、持久层)
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    // 加载 SpringMVC 配置(Web 层)
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMVCConfig.class};
    }

    // 配置 DispatcherServlet 映射,拦截所有请求
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}
3. Controller 注解开发
复制代码
@Controller
@RequestMapping("/user") // 类上路径,所有方法的父路径
public class UserController {

    // 映射路径:/user/hello,GET 请求
    @GetMapping("/hello")
    public String hello(Model model) {
        // 添加模型数据
        model.addAttribute("msg", "Hello, SpringMVC Annotation!");
        // 返回视图名
        return "hello";
    }

    // 直接返回 JSON 数据(前后端分离)
    @GetMapping("/json")
    @ResponseBody
    public User json() {
        return new User(1, "张三", "123456", "zhangsan@example.com");
    }
}

三、注解开发优势

零 XML 配置:减少配置文件,开发效率高

代码简洁:直接在类 / 方法上标记注解,可读性高

类型安全:编译期检查,避免配置错误

符合现代开发:SpringBoot 主流开发方式,无缝衔接


8、Controller 配置总结

一、Controller 核心配置方式对比

配置方式 核心特点 适用场景
XML 配置 传统方式,在 XML 中注册 Bean、映射路径 传统 SSM 项目、复杂配置
注解配置 纯注解,@Controller+@RequestMapping 现代项目、SpringBoot、前后端分离
JavaConfig 配置 纯 Java 配置类,完全替代 XML 纯注解项目、SpringBoot

二、Controller 核心配置要点

1. 路径映射
  • 类上 @RequestMapping("/user"):所有方法的父路径
  • 方法上 @RequestMapping("/hello"):子路径,最终路径 /user/hello
  • @GetMapping/@PostMapping:指定请求方法,避免 GET/POST 混淆
  • 通配符:@RequestMapping("/user/*") 匹配 /user/ 下所有路径
2. 参数绑定
  • 自动绑定:请求参数名与形参名一致,自动赋值
  • @RequestParam:指定参数名,设置 required = false(非必填)、defaultValue(默认值)
  • 实体类绑定:请求参数名与实体类属性一致,自动封装
  • @PathVariable:RESTful 路径参数,如 /user/{id}
3. 数据传递
  • Model/ModelMap:向视图传递模型数据
  • ModelAndView:同时传递模型数据和视图名(传统方式)
  • @SessionAttribute:将数据存入 Session
  • @CookieValue:获取 Cookie 数据
4. 视图跳转
  • 转发:return "视图名"(默认,地址栏不变)
  • 重定向:return "redirect:/路径"(地址栏变化,两次请求)
  • 无视图:@ResponseBody 直接返回 JSON(前后端分离)
5. 异常处理
  • 局部异常:@ExceptionHandler 标注在 Controller 内,处理当前 Controller 异常
  • 全局异常:@ControllerAdvice + @ExceptionHandler,处理所有 Controller 异常

三、Controller 开发最佳实践

RESTful 风格 :使用 @GetMapping/@PostMapping/@PutMapping/@DeleteMapping 设计接口

参数校验 :使用 @Valid + JSR-303 注解,校验请求参数

统一返回格式 :封装统一的返回结果类(如 Result),@ResponseBody 返回 JSON

统一异常处理 :使用 @ControllerAdvice 做全局异常处理,避免代码冗余

拦截器统一处理:使用拦截器做权限校验、日志、统一处理,解耦 Controller

9、RequestMapping 说明

一、核心定义

@RequestMapping是 SpringMVC 最核心的注解之一,用于将 HTTP 请求映射到 Controller 的处理方法 ,可标注在方法上,是请求入口的核心配置。

二、核心属性详解

属性 作用 示例
value/path 指定请求路径(别名,二选一) @RequestMapping("/user")
method 指定请求方法(GET/POST/PUT/DELETE 等) method = RequestMethod.GET
params 指定请求参数条件,满足才映射 params = "id"(必须有 id 参数)
headers 指定请求头条件,满足才映射 headers = "Content-Type=application/json"
consumes 指定请求的 Content-Type consumes = "application/json"
produces 指定响应的 Content-Type produces = "application/json;charset=utf-8"

三、使用示例

1. 类 + 方法组合映射
复制代码
@Controller
@RequestMapping("/user") // 类上路径:所有方法的父路径
public class UserController {

    // 最终路径:/user/hello,支持GET/POST
    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }

    // 最终路径:/user/get,仅支持GET请求
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public String get() {
        return "get";
    }
}
2. 简化请求方法注解(Spring 4.3+)

Spring 提供了@GetMapping/@PostMapping等衍生注解,替代method属性,更简洁:

复制代码
@Controller
@RequestMapping("/user")
public class UserController {
    // 等价于 @RequestMapping(value = "/get", method = RequestMethod.GET)
    @GetMapping("/get")
    public String get() {
        return "get";
    }

    // 等价于 @RequestMapping(value = "/add", method = RequestMethod.POST)
    @PostMapping("/add")
    public String add() {
        return "add";
    }
}

四、路径通配符

  • ?:匹配单个字符,如 /user/? 匹配 /user/a/user/1
  • *:匹配任意多个字符(不包含多级路径),如 /user/* 匹配 /user/abc
  • **:匹配多级路径,如 /user/** 匹配 /user/abc/def

10、RestFul 风格讲解

一、RESTful 核心定义

REST(Representational State Transfer,表述性状态转移)是一种软件架构风格 ,用于设计网络应用的 API,核心是用 URL 定位资源,用 HTTP 方法描述操作,是前后端分离项目的主流 API 设计规范。

二、RESTful 核心设计规范

HTTP 方法 操作 传统 URL RESTful URL 说明
GET 查询 /user/getUserById?id=1 /user/1 查询 ID 为 1 的用户
POST 新增 /user/addUser /user 新增用户
PUT 修改 /user/updateUser /user/1 修改 ID 为 1 的用户
DELETE 删除 /user/deleteUser?id=1 /user/1 删除 ID 为 1 的用户

三、SpringMVC 中 RESTful 实现

1. 核心注解@PathVariable

用于获取 RESTful 路径中的参数,绑定到方法形参:

复制代码
@RestController
@RequestMapping("/user")
public class UserController {

    // GET /user/1 → 查询ID为1的用户
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Integer id) {
        return userService.getUserById(id);
    }

    // POST /user → 新增用户
    @PostMapping
    public User addUser(@RequestBody User user) {
        userService.addUser(user);
        return user;
    }

    // PUT /user/1 → 修改ID为1的用户
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Integer id, @RequestBody User user) {
        user.setId(id);
        userService.updateUser(user);
        return user;
    }

    // DELETE /user/1 → 删除ID为1的用户
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Integer id) {
        userService.deleteUser(id);
    }
}

四、RESTful 优势

URL 简洁:用路径代替参数,可读性高

语义清晰:HTTP 方法对应操作,见名知意

无状态:每个请求独立,适合分布式、微服务

前后端分离友好:统一 API 规范,便于前端调用


11、重定向和转发

一、核心定义与区别

转发(Forward)和重定向(Redirect)是 Web 开发中两种页面跳转方式,核心区别如下:

特性 转发(Forward) 重定向(Redirect)
请求次数 1 次(服务器内部跳转) 2 次(浏览器发起新请求)
地址栏变化 不变 变为新的 URL
数据共享 共享 request 域数据 不共享 request 域数据
跳转范围 仅当前项目内资源 可跳转到任意外部资源
底层实现 服务器内部RequestDispatcher.forward() 浏览器HttpServletResponse.sendRedirect()
SpringMVC 写法 return "视图名"(默认) return "redirect:/路径"

二、SpringMVC 中实现

1. 转发(默认)
复制代码
@Controller
@RequestMapping("/user")
public class UserController {
    @GetMapping("/forward")
    public String forward(Model model) {
        model.addAttribute("msg", "转发测试");
        // 转发到/hello路径,地址栏不变,共享Model数据
        return "hello";
    }
}
2. 重定向
复制代码
@Controller
@RequestMapping("/user")
public class UserController {
    @GetMapping("/redirect")
    public String redirect() {
        // 重定向到/hello路径,地址栏变为/hello,发起新请求
        return "redirect:/hello";
    }
}

三、使用场景

  • 转发:需要共享 request 数据、跳转到项目内页面、避免重复请求
  • 重定向:避免表单重复提交、跳转到外部链接、刷新页面不重复提交

12、接收请求参数及数据回显

一、接收请求参数的方式

SpringMVC 支持多种参数绑定方式,自动将请求参数绑定到方法形参:

1. 基本数据类型 / 字符串

请求参数名与形参名一致,自动绑定:

复制代码
@GetMapping("/get")
public String get(String name, Integer age) {
    // 自动绑定请求参数:?name=张三&age=18
    System.out.println(name + "," + age);
    return "hello";
}
  • 若参数名不一致,用@RequestParam指定:

    @GetMapping("/get")
    public String get(@RequestParam("username") String name) {
    // 绑定请求参数?username=张三到name形参
    return "hello";
    }

2. 实体类绑定

请求参数名与实体类属性一致,自动封装到实体类:

复制代码
@PostMapping("/add")
public String add(User user) {
    // 自动封装请求参数:?name=张三&age=18&email=xxx@xx.com
    System.out.println(user);
    return "hello";
}
3. 数组 / 集合绑定
复制代码
@GetMapping("/batch")
public String batch(String[] ids) {
    // 绑定请求参数:?ids=1&ids=2&ids=3
    return "hello";
}
4. @PathVariable(RESTful 路径参数)
复制代码
@GetMapping("/{id}")
public String get(@PathVariable Integer id) {
    // 绑定路径参数:/user/1 → id=1
    return "hello";
}
5. @RequestBody(JSON 参数)

用于接收 POST 请求的 JSON 格式参数,前后端分离常用:

复制代码
@PostMapping("/add")
public String add(@RequestBody User user) {
    // 接收JSON格式的请求体,自动封装到User
    return "hello";
}

二、数据回显(向视图传递数据)

SpringMVC 提供多种方式向视图传递数据,渲染页面:

1. Model/ModelMap(最常用)
复制代码
@GetMapping("/hello")
public String hello(Model model) {
    // 向request域添加数据,视图可通过${msg}获取
    model.addAttribute("msg", "Hello SpringMVC");
    model.addAttribute("user", new User(1, "张三"));
    return "hello";
}
2. ModelAndView
复制代码
@GetMapping("/hello")
public ModelAndView hello() {
    ModelAndView mv = new ModelAndView();
    // 设置视图名
    mv.setViewName("hello");
    // 添加数据
    mv.addObject("msg", "Hello SpringMVC");
    return mv;
}
3. @SessionAttribute

将数据存入 Session 域,跨请求共享:

复制代码
@GetMapping("/session")
public String session(@SessionAttribute("user") User user) {
    // 从Session获取user数据
    return "hello";
}

13、乱码问题解决

一、常见乱码场景

  • POST 请求参数乱码:POST 请求的中文参数乱码
  • GET 请求参数乱码:GET 请求的中文参数乱码(Tomcat 版本差异)
  • 响应乱码:返回的 JSON / 页面中文乱码

二、解决方案

1. POST 请求乱码(最常见)

配置 Spring 提供的CharacterEncodingFilter,在web.xml中添加:

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>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  • 纯注解开发:在配置类中注册 Filter:

    @Bean
    public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
    FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>();
    CharacterEncodingFilter filter = new CharacterEncodingFilter();
    filter.setEncoding("UTF-8");
    filter.setForceEncoding(true);
    bean.setFilter(filter);
    bean.addUrlPatterns("/*");
    return bean;
    }

2. GET 请求乱码(Tomcat 8 以下)

Tomcat 8 + 已默认 UTF-8,Tomcat 7 及以下需修改conf/server.xml,在 Connector 标签添加URIEncoding="UTF-8"

xml

复制代码
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           URIEncoding="UTF-8"/>
3. 响应 JSON 乱码

@RequestMapping中指定produces,或配置消息转换器:

复制代码
@GetMapping(value = "/json", produces = "application/json;charset=utf-8")
@ResponseBody
public User json() {
    return new User(1, "张三");
}
  • 全局配置:在 SpringMVC 配置中统一消息转换器:

    @Configuration
    public class SpringMVCConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setDefaultCharset(StandardCharsets.UTF_8);
    converters.add(converter);
    }
    }


14、什么是 JSON

一、JSON 核心定义

JSON(JavaScript Object Notation,JavaScript 对象表示法)是一种轻量级数据交换格式,基于 JavaScript 语法,独立于编程语言,是前后端分离、接口交互的主流数据格式。

二、JSON 语法规则

  • 数据以键值对形式存在"key": value
  • 键必须用双引号,值可以是字符串、数字、布尔、数组、对象、null
  • 数据用逗号分隔,最后一个键值对无逗号
  • 对象用{}包裹数组用[]包裹

三、JSON 示例

json

复制代码
{
  "id": 1,
  "name": "张三",
  "age": 18,
  "email": "zhangsan@example.com",
  "hobbies": ["篮球", "音乐"],
  "address": {
    "city": "北京",
    "street": "中关村"
  }
}

四、JSON 优势

轻量简洁:格式简单,易于阅读和编写

跨语言:几乎所有编程语言都支持 JSON 解析

前后端分离友好:完美适配 AJAX 请求,是 RESTful API 的标准数据格式

易于解析:解析速度快,性能高


15、Jackson 使用

一、Jackson 核心定义

Jackson 是 SpringMVC 默认集成的JSON 处理库 ,用于 Java 对象与 JSON 的互相转换,是 SpringMVC@ResponseBody的底层实现,无需额外配置即可使用。

二、核心依赖

xml

复制代码
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>
  • SpringMVC 已默认集成,无需手动导入(spring-webmvc 依赖中包含)

三、核心 API(ObjectMapper)

ObjectMapper是 Jackson 的核心类,用于对象与 JSON 的转换:

复制代码
ObjectMapper mapper = new ObjectMapper();

// 1. Java对象 → JSON字符串
User user = new User(1, "张三", 18);
String json = mapper.writeValueAsString(user);

// 2. JSON字符串 → Java对象
User user = mapper.readValue(json, User.class);

// 3. Java对象 → JSON文件
mapper.writeValue(new File("user.json"), user);

// 4. JSON文件 → Java对象
User user = mapper.readValue(new File("user.json"), User.class);

四、SpringMVC 中使用(@ResponseBody)

SpringMVC 自动集成 Jackson,@ResponseBody注解会自动将返回值转换为 JSON:

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/json")
    public User json() {
        // 自动将User对象转换为JSON,响应给前端
        return new User(1, "张三", 18, "zhangsan@example.com");
    }
}

五、常用注解

注解 作用
@JsonProperty 指定 JSON 字段名,解决属性名与字段名不一致
@JsonIgnore 忽略该属性,不参与 JSON 序列化
@JsonFormat 指定日期格式,如@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonNaming 指定命名策略,如驼峰转下划线

16、Fastjson 使用

一、Fastjson 核心定义

Fastjson 是阿里巴巴开源的高性能 JSON 处理库 ,用于 Java 对象与 JSON 的互相转换,以极快的解析速度著称,是国内常用的 JSON 库。

二、核心依赖

xml

复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

三、核心 API(JSON 类)

JSON是 Fastjson 的核心工具类,提供静态方法实现转换:

复制代码
// 1. Java对象 → JSON字符串
User user = new User(1, "张三", 18);
String json = JSON.toJSONString(user);

// 2. JSON字符串 → Java对象
User user = JSON.parseObject(json, User.class);

// 3. JSON数组 → List集合
String jsonArray = "[{\"id\":1,\"name\":\"张三\"},{\"id\":2,\"name\":\"李四\"}]";
List<User> list = JSON.parseArray(jsonArray, User.class);

// 4. List集合 → JSON数组
String jsonArray = JSON.toJSONString(list);

四、SpringMVC 中集成 Fastjson

替换默认的 Jackson,使用 Fastjson 作为消息转换器:

复制代码
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 创建Fastjson消息转换器
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        // 配置序列化特性
        FastJsonConfig config = new FastJsonConfig();
        config.setCharset(StandardCharsets.UTF_8);
        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        converter.setFastJsonConfig(config);
        // 添加到转换器列表
        converters.add(converter);
    }
}

五、Fastjson vs Jackson 对比

特性 Fastjson Jackson
性能 极快,解析速度领先 快,稳定
Spring 集成 需手动配置 默认集成,零配置
社区维护 曾有安全漏洞,维护活跃 维护稳定,生态完善
使用难度 API 简单,上手快 API 略复杂,功能强大
适用场景 追求极致性能、国内项目 Spring 生态、企业级项目

17、ssm 整合:Mybatis 层

一、MyBatis 层核心目标

将 MyBatis 的核心组件(SqlSessionFactoryMapper、实体类、Mapper.xml)交给 Spring 容器管理,实现数据源统一管理、Mapper 自动注入、事务统一控制

二、MyBatis 层核心配置

1. 项目结构(MyBatis 相关)
复制代码
src/main/java
  └── com.example
      ├── pojo          # 实体类
      │   └── Book.java
      ├── mapper        # Mapper接口
      │   └── BookMapper.java
      └── service       # Service层(后续Spring层)
src/main/resources
  ├── mapper            # Mapper.xml映射文件
  │   └── BookMapper.xml
  ├── mybatis-config.xml # MyBatis核心配置(可简化)
  └── db.properties     # 数据库配置
2. 实体类(Book.java)
复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private Integer id;
    private String bookName;
    private String author;
    private Double price;
}
3. Mapper 接口(BookMapper.java)
复制代码
public interface BookMapper {
    // 查询所有书籍
    List<Book> queryAllBook();
    // 根据ID查询
    Book queryBookById(Integer id);
    // 添加书籍
    int addBook(Book book);
    // 修改书籍
    int updateBook(Book book);
    // 删除书籍
    int deleteBook(Integer id);
    // 搜索书籍
    List<Book> searchBook(String bookName);
}
4. Mapper.xml(BookMapper.xml)
复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.BookMapper">

    <select id="queryAllBook" resultType="Book">
        SELECT * FROM books
    </select>

    <select id="queryBookById" parameterType="int" resultType="Book">
        SELECT * FROM books WHERE id = #{id}
    </select>

    <insert id="addBook" parameterType="Book">
        INSERT INTO books (bookName, author, price)
        VALUES (#{bookName}, #{author}, #{price})
    </insert>

    <update id="updateBook" parameterType="Book">
        UPDATE books
        SET bookName=#{bookName}, author=#{author}, price=#{price}
        WHERE id=#{id}
    </update>

    <delete id="deleteBook" parameterType="int">
        DELETE FROM books WHERE id=#{id}
    </delete>

    <select id="searchBook" parameterType="string" resultType="Book">
        SELECT * FROM books WHERE bookName LIKE CONCAT('%', #{bookName}, '%')
    </select>

</mapper>
5. MyBatis 核心配置(mybatis-config.xml,可简化)
复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <!-- 开启驼峰命名 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启日志 -->
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <!-- 别名配置,Spring层统一配置,此处可省略 -->
    <!-- <typeAliases>
        <package name="com.example.pojo"/>
    </typeAliases> -->
</configuration>

18、ssm 整合:Spring 层

一、Spring 层核心目标

  • 统一管理数据源、SqlSessionFactory、Mapper 扫描
  • 管理 Service 层 Bean,实现依赖注入
  • 配置声明式事务,统一控制 MyBatis 事务

二、Spring 层核心配置

1. 数据库配置(db.properties)

properties

复制代码
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db?useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=root
2. Spring 核心配置(applicationContext.xml)

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
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 1. 加载数据库配置文件 -->
    <context:property-placeholder location="classpath:db.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}"/>
    </bean>

    <!-- 3. 配置SqlSessionFactory(MyBatis核心) -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 加载MyBatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- 加载Mapper.xml -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        <!-- 配置别名 -->
        <property name="typeAliasesPackage" value="com.example.pojo"/>
    </bean>

    <!-- 4. 扫描Mapper接口,自动注册为Bean -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.example.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

    <!-- 5. 扫描Service层 -->
    <context:component-scan base-package="com.example.service"/>

    <!-- 6. 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 7. 开启声明式事务注解支持 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>
3. Service 层接口与实现
复制代码
// BookService.java
public interface BookService {
    List<Book> queryAllBook();
    Book queryBookById(Integer id);
    void addBook(Book book);
    void updateBook(Book book);
    void deleteBook(Integer id);
    List<Book> searchBook(String bookName);
}

// BookServiceImpl.java
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookMapper bookMapper;

    @Override
    public List<Book> queryAllBook() {
        return bookMapper.queryAllBook();
    }

    @Override
    public Book queryBookById(Integer id) {
        return bookMapper.queryBookById(id);
    }

    @Override
    @Transactional // 开启事务
    public void addBook(Book book) {
        bookMapper.addBook(book);
    }

    @Override
    @Transactional
    public void updateBook(Book book) {
        bookMapper.updateBook(book);
    }

    @Override
    @Transactional
    public void deleteBook(Integer id) {
        bookMapper.deleteBook(id);
    }

    @Override
    public List<Book> searchBook(String bookName) {
        return bookMapper.searchBook(bookName);
    }
}

19、ssm 整合:SpringMVC 层

一、SpringMVC 层核心目标

  • 配置 DispatcherServlet,拦截所有请求
  • 扫描 Controller 层,处理 Web 请求
  • 配置视图解析器、乱码过滤器,实现页面跳转与数据响应

二、SpringMVC 层核心配置

1. web.xml(核心入口)

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 1. 配置Spring监听器,启动Spring容器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- 2. 配置乱码过滤器 -->
    <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>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 3. 配置DispatcherServlet(SpringMVC核心入口) -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
2. SpringMVC 配置(springmvc-servlet.xml)

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
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 1. 扫描Controller层 -->
    <context:component-scan base-package="com.example.controller"/>

    <!-- 2. 开启注解驱动 -->
    <mvc:annotation-driven>
        <!-- 配置JSON乱码解决 -->
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="defaultCharset" value="UTF-8"/>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 3. 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 4. 静态资源放行 -->
    <mvc:default-servlet-handler/>

</beans>

20、ssm 整合:查询书籍功能

一、功能目标

实现查询所有书籍,在页面展示书籍列表,是 SSM 整合的第一个完整流程。

二、Controller 层实现

复制代码
@Controller
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    // 查询所有书籍
    @GetMapping("/all")
    public String queryAllBook(Model model) {
        List<Book> bookList = bookService.queryAllBook();
        // 将数据存入Model,传递给视图
        model.addAttribute("bookList", bookList);
        // 跳转到书籍列表页面
        return "bookList";
    }
}

三、页面实现(bookList.jsp)

jsp

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>书籍列表</title>
</head>
<body>
    <h1>书籍列表</h1>
    <a href="${pageContext.request.contextPath}/book/toAdd">添加书籍</a>
    <table border="1" cellpadding="10" cellspacing="0">
        <tr>
            <th>ID</th>
            <th>书名</th>
            <th>作者</th>
            <th>价格</th>
            <th>操作</th>
        </tr>
        <c:forEach items="${bookList}" var="book">
            <tr>
                <td>${book.id}</td>
                <td>${book.bookName}</td>
                <td>${book.author}</td>
                <td>${book.price}</td>
                <td>
                    <a href="${pageContext.request.contextPath}/book/toUpdate?id=${book.id}">修改</a>
                    <a href="${pageContext.request.contextPath}/book/delete/${book.id}">删除</a>
                </td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

四、测试

启动 Tomcat,访问 http://localhost:8080/book/all,即可看到书籍列表,查询功能完成。


21、ssm 整合:添加书籍功能

一、功能目标

实现添加书籍,包含跳转到添加页面、提交表单、保存到数据库、跳转回列表页。

二、Controller 层实现

复制代码
@Controller
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    // 跳转到添加页面
    @GetMapping("/toAdd")
    public String toAdd() {
        return "addBook";
    }

    // 提交添加表单
    @PostMapping("/add")
    public String addBook(Book book) {
        bookService.addBook(book);
        // 重定向到列表页,避免表单重复提交
        return "redirect:/book/all";
    }
}

三、页面实现(addBook.jsp)

jsp

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>添加书籍</title>
</head>
<body>
    <h1>添加书籍</h1>
    <form action="${pageContext.request.contextPath}/book/add" method="post">
        书名:<input type="text" name="bookName"><br>
        作者:<input type="text" name="author"><br>
        价格:<input type="text" name="price"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

22、ssm 整合:修改删除书籍

一、修改书籍功能

1. Controller 层实现
复制代码
@Controller
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    // 跳转到修改页面,根据ID查询书籍
    @GetMapping("/toUpdate")
    public String toUpdate(Integer id, Model model) {
        Book book = bookService.queryBookById(id);
        model.addAttribute("book", book);
        return "updateBook";
    }

    // 提交修改表单
    @PostMapping("/update")
    public String updateBook(Book book) {
        bookService.updateBook(book);
        return "redirect:/book/all";
    }
}
2. 页面实现(updateBook.jsp)

jsp

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>修改书籍</title>
</head>
<body>
    <h1>修改书籍</h1>
    <form action="${pageContext.request.contextPath}/book/update" method="post">
        <input type="hidden" name="id" value="${book.id}">
        书名:<input type="text" name="bookName" value="${book.bookName}"><br>
        作者:<input type="text" name="author" value="${book.author}"><br>
        价格:<input type="text" name="price" value="${book.price}"><br>
        <input type="submit" value="提交修改">
    </form>
</body>
</html>

二、删除书籍功能

1. Controller 层实现
复制代码
@Controller
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    // 删除书籍
    @GetMapping("/delete/{id}")
    public String deleteBook(@PathVariable Integer id) {
        bookService.deleteBook(id);
        return "redirect:/book/all";
    }
}

23、ssm 整合:新增搜索功能

一、功能目标

实现按书名模糊搜索,在列表页添加搜索框,输入书名后筛选结果。

二、Controller 层实现

复制代码
@Controller
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    // 搜索书籍
    @GetMapping("/search")
    public String searchBook(String bookName, Model model) {
        List<Book> bookList = bookService.searchBook(bookName);
        model.addAttribute("bookList", bookList);
        return "bookList";
    }
}

三、页面改造(bookList.jsp,添加搜索框)

jsp

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>书籍列表</title>
</head>
<body>
    <h1>书籍列表</h1>
    <!-- 搜索框 -->
    <form action="${pageContext.request.contextPath}/book/search" method="get">
        <input type="text" name="bookName" placeholder="请输入书名">
        <input type="submit" value="搜索">
        <a href="${pageContext.request.contextPath}/book/all">重置</a>
    </form>

    <a href="${pageContext.request.contextPath}/book/toAdd">添加书籍</a>
    <table border="1" cellpadding="10" cellspacing="0">
        <!-- 表格内容同之前 -->
    </table>
</body>
</html>

24、Ajax 初体验

一、Ajax 核心定义

AJAX(Asynchronous JavaScript and XML,异步 JavaScript 和 XML)是一种异步交互技术,无需刷新整个页面,即可与服务器进行数据交互,实现页面局部刷新,是前后端分离的核心技术。

二、SpringMVC 中 Ajax 实现(以搜索书籍为例)

1. 引入 jQuery(前端依赖)

html

复制代码
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
2. Controller 层(返回 JSON)

java

复制代码
@Controller
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    // Ajax搜索,返回JSON
    @GetMapping("/ajaxSearch")
    @ResponseBody
    public List<Book> ajaxSearch(String bookName) {
        return bookService.searchBook(bookName);
    }
}
3. 前端 Ajax 代码

html

复制代码
<script>
    function searchBook() {
        // 获取输入的书名
        var bookName = $("#searchInput").val();
        // 发送Ajax请求
        $.ajax({
            url: "${pageContext.request.contextPath}/book/ajaxSearch",
            type: "GET",
            data: {bookName: bookName},
            dataType: "json",
            success: function (data) {
                // 清空表格
                $("#bookTable").empty();
                // 拼接表格行
                var html = "";
                for (var i = 0; i < data.length; i++) {
                    var book = data[i];
                    html += "<tr>";
                    html += "<td>" + book.id + "</td>";
                    html += "<td>" + book.bookName + "</td>";
                    html += "<td>" + book.author + "</td>";
                    html += "<td>" + book.price + "</td>";
                    html += "</tr>";
                }
                // 将HTML插入表格
                $("#bookTable").html(html);
            }
        });
    }
</script>

<!-- 搜索框 -->
<input type="text" id="searchInput" placeholder="请输入书名">
<button onclick="searchBook()">搜索</button>

<!-- 表格 -->
<table border="1" cellpadding="10" cellspacing="0">
    <thead>
        <tr>
            <th>ID</th>
            <th>书名</th>
            <th>作者</th>
            <th>价格</th>
        </tr>
    </thead>
    <tbody id="bookTable">
        <!-- 动态加载数据 -->
    </tbody>
</table>

三、Ajax 核心优势

异步交互:无需刷新页面,提升用户体验

局部刷新:仅更新需要变化的部分,减少网络传输

前后端分离:前端负责页面渲染,后端负责接口,分工明确

高性能:减少服务器压力,提升响应速度

25、Ajax 异步加载数据

一、核心定义

Ajax(Asynchronous JavaScript and XML)的核心是异步交互:在不刷新整个页面的前提下,与服务器进行数据通信,实现页面局部数据的动态加载,大幅提升用户体验,是前后端分离的核心技术。

二、异步加载完整示例(书籍列表)

1. Controller 层(返回 JSON 数据)
复制代码
@RestController
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    // 异步加载所有书籍,返回JSON
    @GetMapping("/ajax/list")
    public List<Book> ajaxLoadBookList() {
        return bookService.queryAllBook();
    }
}
2. 前端页面(jQuery 异步加载)
复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Ajax异步加载书籍列表</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <h1>书籍列表(异步加载)</h1>
    <button id="loadBtn">点击加载数据</button>
    <table border="1" cellpadding="10" cellspacing="0" id="bookTable">
        <thead>
            <tr>
                <th>ID</th>
                <th>书名</th>
                <th>作者</th>
                <th>价格</th>
            </tr>
        </thead>
        <tbody>
            <!-- 异步加载数据 -->
        </tbody>
    </table>

    <script>
        $(function () {
            // 点击按钮异步加载数据
            $("#loadBtn").click(function () {
                $.ajax({
                    url: "${pageContext.request.contextPath}/book/ajax/list",
                    type: "GET",
                    dataType: "json",
                    success: function (data) {
                        // 清空表格
                        var tbody = $("#bookTable tbody");
                        tbody.empty();
                        // 遍历数据,拼接表格行
                        for (var i = 0; i < data.length; i++) {
                            var book = data[i];
                            var tr = "<tr>" +
                                "<td>" + book.id + "</td>" +
                                "<td>" + book.bookName + "</td>" +
                                "<td>" + book.author + "</td>" +
                                "<td>" + book.price + "</td>" +
                                "</tr>";
                            tbody.append(tr);
                        }
                    },
                    error: function () {
                        alert("数据加载失败!");
                    }
                });
            });
        });
    </script>
</body>
</html>

三、异步加载核心要点

异步特性async: true(默认),请求发送后不阻塞页面,响应回来后执行回调

局部刷新:仅更新表格数据,页面其他部分不变

前后端解耦:后端只提供 JSON 接口,前端负责页面渲染

性能优化:减少页面整体刷新,降低服务器压力


26、Ajax 验证用户名体验

一、功能目标

实现注册页用户名实时验证:用户输入用户名后,异步请求服务器校验用户名是否已存在,无需提交表单,提升用户体验。

二、完整实现

1. 实体类与 Mapper(User 相关)

java

复制代码
@Data
public class User {
    private Integer id;
    private String username;
    private String password;
}

// UserMapper.java
public interface UserMapper {
    // 根据用户名查询用户
    User queryUserByUsername(String username);
}
2. Service 层

java

复制代码
public interface UserService {
    // 校验用户名是否存在
    boolean checkUsername(String username);
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public boolean checkUsername(String username) {
        return userMapper.queryUserByUsername(username) == null;
    }
}
3. Controller 层

java

复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    // 校验用户名是否可用
    @GetMapping("/checkUsername")
    public Map<String, Object> checkUsername(String username) {
        Map<String, Object> result = new HashMap<>();
        boolean isAvailable = userService.checkUsername(username);
        result.put("code", isAvailable ? 200 : 500);
        result.put("msg", isAvailable ? "用户名可用" : "用户名已存在");
        return result;
    }
}
4. 前端注册页

html

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册页</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <style>.error{color:red;}.success{color:green;}</style>
</head>
<body>
    <h1>用户注册</h1>
    <form action="${pageContext.request.contextPath}/user/register" method="post">
        <div>
            用户名:<input type="text" name="username" id="username">
            <span id="usernameTip"></span>
        </div>
        <div>
            密码:<input type="password" name="password">
        </div>
        <div>
            <input type="submit" value="注册" id="registerBtn">
        </div>
    </form>

    <script>
        $(function () {
            // 用户名输入框失去焦点时,异步校验
            $("#username").blur(function () {
                var username = $(this).val();
                if (username == "") {
                    $("#usernameTip").text("用户名不能为空").addClass("error");
                    return;
                }
                // 发送Ajax请求
                $.ajax({
                    url: "${pageContext.request.contextPath}/user/checkUsername",
                    type: "GET",
                    data: {username: username},
                    dataType: "json",
                    success: function (data) {
                        if (data.code == 200) {
                            $("#usernameTip").text(data.msg).removeClass("error").addClass("success");
                            $("#registerBtn").prop("disabled", false);
                        } else {
                            $("#usernameTip").text(data.msg).removeClass("success").addClass("error");
                            $("#registerBtn").prop("disabled", true);
                        }
                    }
                });
            });
        });
    </script>
</body>
</html>

27、拦截器是什么

一、核心定义

SpringMVC 拦截器(Interceptor)是 SpringMVC 提供的AOP 增强组件 ,用于拦截 Controller 的请求,在请求处理的前置、后置、完成阶段执行自定义逻辑,类似 Servlet 的 Filter,但更聚焦于 SpringMVC 请求链路。

二、核心方法与执行流程

拦截器需实现HandlerInterceptor接口,核心 3 个方法:

方法 执行时机 作用
preHandle() Controller 方法执行 权限校验、登录判断、日志记录,返回true放行,false拦截
postHandle() Controller 方法执行 、视图渲染 修改 Model 数据、统一响应处理
afterCompletion() 视图渲染完成后 资源释放、日志记录、异常处理

三、自定义拦截器实现(登录拦截)

复制代码
@Component
public class LoginInterceptor implements HandlerInterceptor {

    // 前置拦截:登录校验
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从Session获取用户信息
        User user = (User) request.getSession().getAttribute("loginUser");
        // 未登录,重定向到登录页
        if (user == null) {
            response.sendRedirect(request.getContextPath() + "/user/toLogin");
            return false;
        }
        // 已登录,放行
        return true;
    }

    // 后置处理
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 可修改Model数据
    }

    // 完成后处理
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 资源释放
    }
}

四、拦截器配置(SpringMVC 配置)

xml

复制代码
<!-- springmvc-servlet.xml -->
<mvc:interceptors>
    <!-- 配置登录拦截器,拦截所有请求 -->
    <mvc:interceptor>
        <!-- 拦截所有请求 -->
        <mvc:mapping path="/**"/>
        <!-- 排除登录相关请求 -->
        <mvc:exclude-mapping path="/user/toLogin"/>
        <mvc:exclude-mapping path="/user/login"/>
        <mvc:exclude-mapping path="/user/checkUsername"/>
        <!-- 引用拦截器Bean -->
        <ref bean="loginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

五、拦截器 vs 过滤器

特性 拦截器(Interceptor) 过滤器(Filter)
所属框架 SpringMVC Servlet 原生
拦截范围 仅拦截 SpringMVC 请求(Controller) 拦截所有 Web 请求(包括静态资源)
依赖容器 依赖 Spring 容器 依赖 Servlet 容器
执行时机 DispatcherServlet 之后,Controller 之前 Servlet 之前,所有请求入口
适用场景 登录校验、权限控制、SpringMVC 请求增强 乱码过滤、全局请求处理

28、登录判断验证

一、功能目标

结合拦截器实现完整登录流程:登录页、登录校验、Session 存储、拦截器校验、退出登录。

二、完整实现

1. Controller 层(登录相关)
复制代码
@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    // 跳转到登录页
    @GetMapping("/toLogin")
    public String toLogin() {
        return "login";
    }

    // 登录校验
    @PostMapping("/login")
    public String login(String username, String password, HttpSession session, Model model) {
        User user = userService.login(username, password);
        if (user != null) {
            // 登录成功,将用户存入Session
            session.setAttribute("loginUser", user);
            // 重定向到书籍列表页
            return "redirect:/book/all";
        } else {
            // 登录失败,返回登录页并提示
            model.addAttribute("msg", "用户名或密码错误");
            return "login";
        }
    }

    // 退出登录
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        // 销毁Session
        session.invalidate();
        // 重定向到登录页
        return "redirect:/user/toLogin";
    }
}
2. 登录页(login.jsp)

html

复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页</title>
</head>
<body>
    <h1>用户登录</h1>
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        <div style="color: red">${msg}</div>
        <div>
            用户名:<input type="text" name="username">
        </div>
        <div>
            密码:<input type="password" name="password">
        </div>
        <div>
            <input type="submit" value="登录">
        </div>
    </form>
</body>
</html>
3. 拦截器校验(见 27 章)

拦截器会拦截所有非登录请求,未登录用户自动跳转到登录页,实现登录判断验证。


29、文件上传和下载回顾

一、文件上传

1. 核心依赖与配置

xml

复制代码
<!-- 文件上传依赖(SpringMVC已集成) -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

SpringMVC 配置文件上传解析器:

xml

复制代码
<!-- springmvc-servlet.xml -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 最大上传大小:10MB -->
    <property name="maxUploadSize" value="10485760"/>
    <!-- 编码 -->
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
2. Controller 层(文件上传)

java

复制代码
@Controller
@RequestMapping("/file")
public class FileController {
    // 文件上传
    @PostMapping("/upload")
    public String upload(MultipartFile file, HttpServletRequest request) throws IOException {
        // 获取文件名
        String fileName = file.getOriginalFilename();
        // 获取项目真实路径
        String realPath = request.getServletContext().getRealPath("/upload/");
        // 创建目录
        File dir = new File(realPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        // 保存文件
        file.transferTo(new File(realPath + fileName));
        return "redirect:/file/toUpload";
    }
}
3. 上传页

html

复制代码
<form action="${pageContext.request.contextPath}/file/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="上传">
</form>

二、文件下载

1. Controller 层(文件下载)

java

复制代码
@GetMapping("/download")
public ResponseEntity<byte[]> download(String fileName, HttpServletRequest request) throws IOException {
    // 获取文件路径
    String realPath = request.getServletContext().getRealPath("/upload/");
    File file = new File(realPath + fileName);
    // 读取文件
    byte[] bytes = FileUtils.readFileToByteArray(file);
    // 设置响应头
    HttpHeaders headers = new HttpHeaders();
    headers.setContentDispositionFormData("attachment", fileName, StandardCharsets.UTF_8);
    // 返回文件
    return ResponseEntity.ok()
            .headers(headers)
            .body(bytes);
}

30、总结和展望

一、SSM 全栈知识体系总结

1. MyBatis(持久层)
  • 核心:ORM 映射、SQL 与代码分离、动态 SQL、缓存(一级 / 二级)
  • 整合:SqlSessionFactoryMapperScannerConfigurer、事务管理
2. Spring(业务层)
  • 核心:IOC/DI、AOP、声明式事务、Bean 管理
  • 整合:统一管理数据源、MyBatis 组件、Service 层、事务
3. SpringMVC(Web 层)
  • 核心:MVC 架构、DispatcherServlet、请求映射、参数绑定、视图解析
  • 功能:拦截器、文件上传下载、Ajax、登录验证、异常处理
4. SSM 完整架构流转
复制代码
用户请求 → SpringMVC(Controller,接收请求、参数校验)
          ↓
          Spring(Service,业务逻辑、事务管理、AOP增强)
          ↓
          MyBatis(Mapper,数据库操作、ORM映射)
          ↓
          MySQL(数据库,数据存储、事务执行)
          ↓
          反向响应 → Service → Controller → 视图/JSON → 用户

二、高频面试题汇总

  1. SpringMVC 的执行流程?
    • DispatcherServlet拦截请求 → HandlerMapping找 Controller → HandlerAdapter执行方法 → 返回ModelAndViewViewResolver解析视图 → 渲染响应。
  2. 拦截器和过滤器的区别?
    • 拦截器是 SpringMVC 组件,仅拦截 Controller 请求;过滤器是 Servlet 组件,拦截所有请求。
  3. Ajax 的核心原理?
    • 异步 JavaScript 和 XML,通过XMLHttpRequest对象异步请求服务器,实现页面局部刷新。
  4. SSM 整合的核心是什么?
    • 将 MyBatis、SpringMVC 的组件全部交给 Spring 容器统一管理,实现事务、依赖注入的统一控制。
  5. Spring 声明式事务的原理?
    • 基于 AOP 动态代理,在方法执行前后开启、提交 / 回滚事务,无需手动编写事务代码。

三、技术学习展望

  1. SpringBoot:零 XML 配置,快速开发,是 SSM 的升级替代方案
  2. MyBatis-Plus:MyBatis 增强工具,简化 CRUD,提升开发效率
  3. SpringCloud:微服务架构,实现分布式系统开发
  4. 前后端分离:Vue/React + SpringBoot,纯 RESTful API 开发
  5. 容器化部署:Docker + Kubernetes,实现项目快速部署与运维
相关推荐
海海不掉头发3 小时前
【11月16日-大模型前置知识【深度学习】+大模型开发入门】-基础篇笔记
人工智能·笔记·深度学习
ZhiqianXia3 小时前
PyTorch 学习笔记(18) : lowering.py
pytorch·笔记·学习
一定要AK3 小时前
HTML5 入门到精通全章节学习笔记
笔记·学习·html5
Engineer邓祥浩4 小时前
JVM学习笔记(7) 第三部分 虚拟机执行子系统 第6章 类文件结构
jvm·笔记·学习
中屹指纹浏览器4 小时前
2026指纹浏览器技术架构深度解析:从隔离原理到性能优化的全链路实践
经验分享·笔记
sheeta19984 小时前
LeetCode 每日一题笔记 日期:2026.04.09 题目:3655.区间乘法查询后的异或二
笔记·算法·leetcode
NULL指向我4 小时前
nanomodbus库移植笔记1:移植创建slave从机实例
笔记
ZhiqianXia4 小时前
PyTorch 学习笔记(13):third_party 第三方依赖全景图
pytorch·笔记·学习
智者知已应修善业4 小时前
【51单片机利用外部中断编写程序用两个按键控制数码管显示从0到9,S1控制加计数0—9,S2控制减计数9—0。】
c语言·经验分享·笔记·算法·51单片机