第3章 Spring Boot的Web应用支持,个人学习笔记

1了解使用注解扫描注册Java Web三大组件,能够简述使用注解扫描注册Java Web三大组件的步骤

2掌握自定义Spring MVC配置,能够自定义配置Spring MVC中的静态资源映射、视图控制器、拦截器

3掌握自定义Spring MVC配置,能够自定义配置Spring MVC中的静态资源映射、视图控制器、拦截器

4掌握文件上传,能够在Spring Boot项目中实现文件上传

5掌握Spring Boot自定义异常处理,能够在Spring Boot项目中自定义异常处理


Spring Boot 注册 Servlet / Filter / Listener 三大组件详解

Spring Boot (底层基于 Servlet 容器:Tomcat、Jetty 等)中,Servlet、Filter、Listener 是 Java Web 原生的三大核心组件,Spring Boot 对其做了自动化配置简化,我们先搞懂每个组件的核心作用,再讲 Spring Boot 中如何注册使用。


一、三大组件核心作用(通俗 + 专业版)

1. Servlet ------ 请求处理器(核心)

专业定义 :处理客户端请求,生成响应结果的核心组件,是 Web 请求的「真正执行者」。通俗理解 :浏览器 / 客户端发送请求 → Servlet 接收请求 → 执行业务逻辑(查库、计算)→ 返回响应(页面 / JSON 数据)。

核心职责

  • 接收 HttpServletRequest 请求
  • 处理业务逻辑
  • 封装 HttpServletResponse 响应
  • 是 Spring MVC DispatcherServlet 的底层基础

2. Filter ------ 过滤器(请求拦截器)

专业定义 :对请求和响应进行拦截预处理 / 后处理 ,基于链式调用,不主动生成响应。通俗理解 :请求到达 Servlet 之前 → Filter 先拦截(做校验、过滤、增强)→ 放行后才到 Servlet;响应返回客户端之前 → Filter 还能再次拦截处理。

核心职责

  • 统一编码设置(UTF-8)
  • 请求权限校验(登录验证)
  • 敏感词过滤
  • 请求参数加密 / 解密
  • 日志记录、性能监控

特点:双向拦截(请求进、响应出),可拦截所有 Web 资源。


3. Listener ------ 监听器(事件监听者)

专业定义 :监听 Web 应用中域对象的创建、销毁、属性变更 等事件,自动触发回调。通俗理解 :Web 容器发生特定事件(如服务启动、会话创建)→ Listener 自动感知并执行代码,无需手动调用。

核心职责

  • 监听应用启动 / 关闭(项目初始化加载数据)
  • 监听用户会话(Session)创建 / 销毁(统计在线人数)
  • 监听请求域属性变更
  • 系统初始化、资源释放、全局日志埋点

二、三者关系(一句话总结)

  1. Listener 监听容器生命周期(出生 / 死亡)
  2. Filter 拦截请求,做前置 / 后置处理
  3. Servlet 最终处理请求,生成响应

执行顺序:Web 应用启动(Listener 触发) → 请求到达 → Filter 拦截Servlet 处理 → Filter 后置处理 → 响应返回


三、Spring Boot 中注册三大组件(两种方式)

Spring Boot 没有 web.xml,推荐两种注册方式:

方式 1:注解版(最简单,推荐)

直接在组件类上加注解,启动类开启扫描即可。

1. 注册 Servlet

java

运行

复制代码
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 注解声明 Servlet,绑定访问路径
@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // 处理请求
    }
}
2. 注册 Filter

java

运行

复制代码
import javax.servlet.annotation.WebFilter;
import javax.servlet.*;
import java.io.IOException;

// 拦截所有请求
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        // 1. 请求预处理
        chain.doFilter(request, response); // 放行
        // 2. 响应后处理
    }
}
3. 注册 Listener

java

运行

复制代码
import javax.servlet.ServletContextEvent;
import javax.servlet.annotation.WebListener;
import javax.servlet.ServletContextListener;

// 监听应用启动/关闭
@WebListener
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 项目启动时执行(初始化资源)
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 项目关闭时执行(释放资源)
    }
}
最后:启动类开启组件扫描

java

运行

复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan // 扫描 @WebServlet/@WebFilter/@WebListener 注解
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

方式 2:配置类注册(无注解,灵活控制)

创建配置类,手动注册 Bean,适用于第三方组件无法修改源码的场景。

java

运行

复制代码
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebComponentConfig {

    // 1. 注册 Servlet
    @Bean
    public ServletRegistrationBean<MyServlet> myServlet() {
        ServletRegistrationBean<MyServlet> bean = new ServletRegistrationBean<>();
        bean.setServlet(new MyServlet());
        bean.addUrlMappings("/myServlet");
        return bean;
    }

    // 2. 注册 Filter
    @Bean
    public FilterRegistrationBean<MyFilter> myFilter() {
        FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new MyFilter());
        bean.addUrlPatterns("/*");
        return bean;
    }

    // 3. 注册 Listener
    @Bean
    public ServletListenerRegistrationBean<MyListener> myListener() {
        ServletListenerRegistrationBean<MyListener> bean = new ServletListenerRegistrationBean<>();
        bean.setListener(new MyListener());
        return bean;
    }
}

四、高频面试题总结

  1. Servlet 作用:处理请求,生成响应。
  2. Filter 作用:拦截请求,做统一预处理 / 后处理。
  3. Listener 作用:监听 Web 事件,自动执行初始化 / 销毁逻辑。
  4. 执行顺序:Listener 启动 → Filter 拦截 → Servlet 处理。
  5. Spring Boot 注册@ServletComponentScan + 原生注解 最简便。

总结

  1. Servlet:处理请求、生成响应,是 Web 请求的核心处理器
  2. Filter:拦截请求 / 响应,做统一校验、编码、过滤等增强操作
  3. Listener:监听容器生命周期,自动执行初始化、资源释放等逻辑
  4. Spring Boot 中用 @ServletComponentScan 可一键注册三大原生组件

完成 Spring MVC 自定义配置,包含你要的三大功能:

  1. 静态资源映射(图片、CSS、JS 等)
  2. 视图控制器(直接页面跳转,不用写 Controller)
  3. 拦截器(登录校验、日志等)

全程复制粘贴就能用,我会讲清楚每一步做什么。


第一步:创建配置类(核心)

创建一个类,实现 WebMvcConfigurer 接口这是 Spring Boot 推荐的自定义 Spring MVC 配置方式

java

运行

复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Spring MVC 扩展配置
 * 实现 WebMvcConfigurer 即可自定义:静态资源、视图、拦截器、消息转换器等
 */
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

}

第二步:添加 静态资源映射(最常用)

作用:让 Spring MVC 识别你的图片、JS、CSS、HTML 等静态资源

在配置类里添加方法:

java

运行

复制代码
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    // 1. 访问路径:/images/**      映射到    项目里的:classpath:/images/
    registry.addResourceHandler("/images/**")
            .addResourceLocations("classpath:/images/");

    // 2. 访问路径:/css/**         映射到    classpath:/css/
    registry.addResourceHandler("/css/**")
            .addResourceLocations("classpath:/css/");

    // 3. 访问路径:/js/**          映射到    classpath:/js/
    registry.addResourceHandler("/js/**")
            .addResourceLocations("classpath:/js/");

    // 4. 页面(可选)
    registry.addResourceHandler("/pages/**")
            .addResourceLocations("classpath:/pages/");
}

作用说明

  • 浏览器访问 http://localhost:8080/images/1.jpg
  • 自动去项目 resources/images/1.jpg 找文件

第三步:添加 视图控制器(直接跳转页面)

不用写 Controller,直接让 URL 映射到页面

java

运行

复制代码
@Override
public void addViewControllers(ViewControllerRegistry registry) {
    // 访问 /index  → 跳转到 index.html
    registry.addViewController("/index").setViewName("index.html");

    // 访问 /login  → 跳转到 login.html
    registry.addViewController("/login").setViewName("login.html");

    // 访问 /home   → 跳转到 home.html
    registry.addViewController("/home").setViewName("home.html");
}

效果

访问:

plaintext

复制代码
http://localhost:8080/index

直接显示页面,不需要写 Controller


第四步:添加 拦截器(最关键)

4.1 先创建一个拦截器类

java

运行

复制代码
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * 自定义拦截器:登录校验、日志、权限等
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    // 在Controller执行之前执行
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        System.out.println("拦截器生效:" + request.getRequestURI());

        // 这里可以写登录校验
        // return false 表示拦截
        // return true  表示放行

        return true;
    }
}

4.2 把拦截器注册到 Spring MVC

回到 MyMvcConfig 配置类,添加:

java

运行

复制代码
@Autowired
private LoginInterceptor loginInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(loginInterceptor)
            .addPathPatterns("/**")          // 拦截所有请求
            .excludePathPatterns(            // 放行不拦截
                    "/login",
                    "/register",
                    "/static/**",
                    "/images/**",
                    "/css/**",
                    "/js/**",
                    "/*.html"
            );
}

1. 启动项目

控制台无报错 = 配置成功

2. 测试静态资源

resources 下创建

  • images
  • css
  • js

放入 1.jpg访问:

plaintext

复制代码
http://localhost:8080/images/1.jpg

能显示图片 = 成功

3. 测试视图控制器

访问:

plaintext

复制代码
http://localhost:8080/index

直接跳转到页面 = 成功

4. 测试拦截器

访问任何接口,控制台打印:

plaintext

复制代码
拦截器生效:/xxx

= 拦截器成功


实现 Spring Boot 文件上传 ,从代码到测试,全程复制粘贴就能跑通,保证一次成功!

一、先记住:Spring Boot 文件上传超简单

只需要 3 步:

  1. 写一个 接收文件的接口
  2. 配置 上传大小限制
  3. 测试上传

我现在直接带你写完整代码!


第一步:创建文件上传接口(核心代码)

直接新建一个 FileController

java

运行

复制代码
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

@RestController
public class FileController {

    // 文件上传保存的目录(自己改路径)
    private static final String UPLOAD_DIR = "C:/upload/";

    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        // 1. 判断文件是否为空
        if (file.isEmpty()) {
            return "上传失败,请选择文件";
        }

        // 2. 获取文件原始名字
        String originalFilename = file.getOriginalFilename();

        // 3. 生成唯一文件名(防止重名覆盖)
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        String newFileName = UUID.randomUUID() + suffix;

        // 4. 创建目标文件
        File destFile = new File(UPLOAD_DIR + newFileName);

        // 如果目录不存在,自动创建
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }

        // 5. 上传(把内存文件写到磁盘)
        try {
            file.transferTo(destFile);
            return "上传成功!路径:" + destFile.getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
            return "上传失败:" + e.getMessage();
        }
    }
}

第二步:配置文件上传大小(application.yml/properties)

推荐用 yml 格式:

yaml

复制代码
spring:
  servlet:
    multipart:
      max-file-size: 10MB        # 单个文件最大 10MB
      max-request-size: 100MB    # 一次请求总文件最大 100MB
      enabled: true              # 开启文件上传

如果是 properties 格式:

properties

复制代码
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
spring.servlet.multipart.enabled=true

第三步:创建上传目录

在电脑 C 盘 新建一个文件夹

plaintext

复制代码
C:\upload

确保有写入权限。


第四步:测试上传(最简单)

方法 1:用 Postman 测试

  1. 打开 Postman
  2. 请求方式选 POST
  3. URL 输入:http://localhost:8080/upload
  4. Body → form-data
  5. Key 输入:file
  6. Value 选择一个图片 / 文件

点击发送 → 返回上传成功!


方法 2:用网页表单测试(不用 Postman)

resources/static 下新建 upload.html

html

预览

复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
    <h1>Spring Boot 文件上传</h1>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <button type="submit">上传</button>
    </form>
</body>
</html>

访问:

plaintext

复制代码
http://localhost:8080/upload.html

选文件 → 上传 → 成功!

你现在已经实现了 ✅

  • 单文件上传
  • 自动生成唯一文件名
  • 自动创建目录
  • 文件大小限制
  • 简单易用的接口

我可以继续带你升级功能(你想要哪个?)


二、一共 4 步,带你彻底搞定

步骤 1:统一返回结果类(前后端交互必备)

创建一个 Result 类,所有接口成功 / 失败都用它返回。

java

运行

复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private int code;       // 响应码 200成功 500错误
    private String msg;     // 提示信息
    private T data;         // 返回数据

    // 成功
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }
    public static <T> Result<T> success() {
        return success(null);
    }

    // 失败
    public static <T> Result<T> fail(String msg) {
        return new Result<>(500, msg, null);
    }
    public static <T> Result<T> fail(int code, String msg) {
        return new Result<>(code, msg, null);
    }
}

步骤 2:自定义业务异常类(主动抛出异常)

专门用来抛业务错误,比如:登录失败、参数错误。

java

运行

复制代码
import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {
    private final int code;

    // 自定义异常信息
    public BusinessException(String msg) {
        super(msg);
        this.code = 500;
    }

    // 自定义异常码 + 信息
    public BusinessException(int code, String msg) {
        super(msg);
        this.code = code;
    }
}

步骤 3:全局异常处理器(最重要!)

@RestControllerAdvice 统一捕获所有异常。

java

运行

复制代码
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

// 全局异常捕获
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 1. 捕获我们自定义的业务异常
    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusinessException(BusinessException e) {
        return Result.fail(e.getCode(), e.getMessage());
    }

    // 2. 捕获所有其他异常(系统未知错误)
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        e.printStackTrace(); // 打印错误日志
        return Result.fail("系统异常,请联系管理员");
    }
}

步骤 4:测试异常(Controller 测试)

写一个测试接口,主动抛异常看效果。

java

运行

复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/test")
    public Result<String> test() {
        // 模拟业务异常(比如:用户名不存在)
        throw new BusinessException("用户不存在!");
    }
}

三、启动项目测试

访问:

plaintext

复制代码
http://localhost:8080/test

返回结果(统一 JSON 格式):

json

复制代码
{
  "code": 500,
  "msg": "用户不存在!",
  "data": null
}

自定义异常处理成功!


四、扩展:常用异常你都能处理

我给你直接写好,复制就能用:

1. 参数校验异常

java

运行

复制代码
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValidException(MethodArgumentNotValidException e) {
    String msg = e.getBindingResult().getFieldError().getDefaultMessage();
    return Result.fail(400, msg);
}

2. 空指针异常

java

运行

复制代码
@ExceptionHandler(NullPointerException.class)
public Result<?> handleNullPointerException(NullPointerException e) {
    return Result.fail(500, "空指针异常");
}

3. 文件上传过大异常

java

运行

复制代码
@ExceptionHandler(MaxUploadSizeExceededException.class)
public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
    return Result.fail(400, "文件大小超出限制!");
}

五、你现在彻底掌握了


你已经学会 Spring Boot 全局异常处理!

接下来你想让我带你做什么?

  1. 多文件上传

  2. 上传后返回可访问的图片 URL

  3. 图片上传 + 预览

  4. 文件大小、类型校验(只允许图片)

  5. 上传到阿里云 OSS / 腾讯云 COS



    超清晰 带你掌握 Spring Boot 全局自定义异常处理,这是面试必问、项目必用的核心技能,全程复制粘贴就能用!

    一、先搞懂:自定义异常处理有什么用?

  6. 统一项目的错误返回格式(全部返回 JSON,不再返回杂乱页面)

  7. 捕获所有 Controller 的异常,不用每个方法写 try-catch

  8. 自定义业务异常(比如:用户名不存在、密码错误、未登录)

  9. 让项目更优雅、更好维护

  10. 统一返回结果

  11. 整合 MyBatis Plus

  12. 登录拦截 + Token

  13. 完整后台管理系统

  14. 学习 AOP 面向切面编程

    • 自定义业务异常
    • 全局异常捕获
    • 各种系统异常处理
相关推荐
bugcome_com2 小时前
【ASP.NET Web Pages】页面布局核心实战:从复用性到安全性,打造一致化网站界面
前端·后端·asp.net
Sylus_sui2 小时前
Class 模型 + 跨组件状态(@Observed)+ 网络请求封装 + 本地存储全部是鸿蒙 Next/Stage 模型标准写法
前端
代码栈上的思考2 小时前
消息队列持久化:文件存储设计与实现全解析
java·前端·算法
weixin_443478512 小时前
flutter组件学习之卡片与列表
javascript·学习·flutter
召田最帅boy2 小时前
SpringBoot实现AI智能评论审核与自动回复
人工智能·spring boot·后端·架构
踩着两条虫2 小时前
去“AI味儿”实操手册:从“机器脸”到“高级脸”,只差这三步!
前端·vue.js·ai编程
Luna-player2 小时前
[特殊字符] Spring Boot 静态资源默认映射规则详解
学习
苦瓜小生2 小时前
【黑马点评学习笔记 | 实战篇 】| 7-达人探店
redis·笔记·后端·学习
qq_211387472 小时前
基于LangGraph多agent
开发语言·前端·javascript·agent·langgraph