Java进阶全套教程(四)—— SpringMVC框架详解

Java进阶全套教程(四)------ SpringMVC框架详解

一、SpringMVC核心认知与企业定位

SpringMVC是Spring生态原生的MVC分层Web框架,是目前Java后端Web开发的绝对主流框架,所有SpringBoot Web项目、SpringCloud微服务接口项目的底层Web核心均为SpringMVC。它基于Spring IOC、AOP核心特性构建,完美适配Spring全家桶生态,彻底替代老旧的Struts2框架,解决了原生Servlet开发代码冗余、耦合严重、参数解析繁琐、视图跳转混乱、无统一异常处理的核心痛点。

SpringMVC严格遵循MVC架构设计模式 ,实现数据、视图、业务控制的彻底解耦:Model(模型层)负责封装业务数据与业务逻辑、View(视图层)负责数据展示、Controller(控制层)负责请求分发与流程调度。相较于原生Servlet,SpringMVC提供了全自动参数绑定、统一响应封装、全局异常处理、拦截器、文件操作、跨域支持等企业级通用能力,大幅提升Web项目开发效率与代码规范性。

本教程聚焦Spring6适配的最新SpringMVC特性、底层运行原理、全场景实战、生产避坑、企业规范配置 ,摒弃老旧XML冗余配置、过时API和应试习题,全部案例采用纯注解开发+自定义业务场景,代码原创可直接用于生产项目,适配单体Web项目、前后端分离接口项目、微服务网关接口场景。

官方权威地址 :SpringMVC官方文档 https://docs.spring.io/spring-framework/reference/web/webmvc.html

二、SpringMVC整体架构与执行流程(底层核心)

2.1 核心组件详解(企业必备)

SpringMVC整套框架基于七大核心组件协同工作,组件职责单一、各司其职,理解组件协作机制是吃透SpringMVC底层的关键,所有Web请求的处理流程均围绕这些组件展开:

  • DispatcherServlet(前端控制器) :SpringMVC的核心中枢,全局唯一入口,所有客户端请求(浏览器、Ajax、Postman)都会先经过该组件,负责统一接收请求、分发请求、调度其他组件、响应结果,相当于整个框架的"总指挥"。

  • HandlerMapping(处理器映射器):请求路由匹配组件,负责根据客户端请求的URL地址,匹配对应的Controller处理器方法,返回处理器执行链,解决"请求找哪个方法处理"的问题。

  • HandlerAdapter(处理器适配器):方法执行适配组件,SpringMVC支持多种处理器写法,适配器负责适配不同类型的处理器,统一调用规则,执行目标Controller方法,解决"如何执行找到的方法"的问题。

  • Handler(处理器):开发者自定义的Controller控制器方法,是真正处理业务逻辑的核心,负责接收参数、调用Service业务层、封装返回数据。

  • ViewResolver(视图解析器):视图渲染组件,负责解析视图路径、拼接视图前缀后缀、匹配视图资源,完成服务端页面跳转渲染,适配JSP、Thymeleaf等视图模板。

  • View(视图):数据展示载体,如JSP页面、HTML页面,负责渲染Controller返回的模型数据,展示给客户端。

  • ModelAndView(模型视图):数据与视图封装对象,统一封装业务数据(Model)和视图名称(View),实现数据与视图的绑定传递。

2.2 完整请求执行流程(图解式梳理)

SpringMVC的请求工作流程是整个框架的核心底层原理 ,也是面试高频核心考点、线上接口异常排查的核心依据。其整体运行严格遵循客户端 → Web容器(Tomcat) → SpringMVC核心组件联动 → 业务处理 → 视图/数据渲染 → 客户端响应的标准化链路。

结合Spring官方规范,一次完整的HTTP请求从发起、处理到响应结束,共分为10个标准步骤,全程由SpringMVC九大核心组件自动协同完成,开发者仅需专注业务逻辑开发,下面结合组件职责、底层机制、参数流转做逐行精细化拆解:

步骤1:请求接入容器,交由前端控制器统一接收

客户端(浏览器、Postman、前端Axios等)发起HTTP/HTTPS请求,请求首先被Web容器(Tomcat)捕获。Tomcat读取请求报文、封装原生HttpServletRequest和HttpServletResponse对象,根据项目Servlet映射规则,将所有请求统一转发给SpringMVC的核心总入口 DispatcherServlet(前端控制器)

DispatcherServlet是整个SpringMVC的唯一全局入口、流程总指挥,所有请求必须经过它统一调度,彻底实现请求集中管控,规避原生Servlet分散处理的混乱问题。

步骤2:处理器映射器完成URL路由匹配

DispatcherServlet接收请求后,不直接处理业务,而是调用 HandlerMapping(处理器映射器) 进行路由解析。映射器会读取当前请求的URL、请求方式,扫描项目中所有标注@RequestMapping、@GetMapping、@PostMapping的Controller接口,完成精准匹配。

匹配成功后,HandlerMapping会封装处理器执行链(HandlerExecutionChain),包含目标Controller方法、拦截器链等信息;若匹配失败(无对应接口),直接返回404请求资源不存在。该步骤解决了「请求该交给哪个方法处理」的核心问题。

步骤3:处理器适配器适配执行规则

DispatcherServlet拿到处理器执行链后,调用HandlerAdapter(处理器适配器) 。由于SpringMVC支持多种处理器写法(注解式Controller、传统Controller、函数式端点等),不同处理器的执行规则不同,适配器的核心作用是统一适配、统一调用规范,屏蔽底层差异,让框架可以通用执行所有类型的处理器方法。

适配完成后,适配器准备执行目标Controller方法,是衔接路由匹配与业务执行的关键中间组件。

步骤4:全自动参数解析、类型转换与参数绑定(核心能力)

这是SpringMVC相较于原生Servlet最大的优势所在。HandlerAdapter会调用内置的参数解析器 ,自动完成全流程参数处理:解析URL参数、表单参数、JSON请求体、路径变量,自动完成数据类型转换、日期格式化、参数校验、对象封装

最终将处理完成的合法参数,自动注入到目标Controller方法的入参中,全程无需开发者手动获取request、手动转型、手动封装对象,彻底简化参数处理逻辑。

步骤5:执行控制器业务逻辑,封装模型视图数据

参数注入完成后,适配器正式执行目标Controller的业务方法。开发者自定义的Controller逻辑开始执行,调用Service业务层、Dao持久层,完成数据库查询、新增、修改、删除等业务操作,处理业务规则、权限校验、数据组装。

业务执行完成后,Controller会返回数据(Model模型数据)和视图名称,SpringMVC自动将其封装为 ModelAndView 对象:Model存储业务响应数据,View存储视图跳转路径,实现数据与视图的绑定。

步骤6:视图解析器解析真实视图路径

Controller执行完毕,HandlerAdapter将ModelAndView对象回传给核心总指挥DispatcherServlet。随后DispatcherServlet调用 ViewResolver(视图解析器),根据配置的视图前缀、后缀,对ModelAndView中的视图名称进行解析,拼接出完整、真实的服务端视图资源路径(如JSP、Thymeleaf模板页面路径)。

如果是前后端分离场景(使用@RestController),方法直接返回JSON数据,不会进入视图解析流程,直接响应数据给客户端。

步骤7:视图渲染,数据填充页面

视图解析器匹配到真实View视图资源后,启动视图渲染流程。框架将Model中封装的所有业务数据,自动填充到视图页面的对应位置,完成页面数据渲染、动态内容替换,生成完整的静态页面内容。

步骤8:统一封装HTTP响应,返回客户端

视图渲染完成后,DispatcherServlet将渲染后的完整页面、或者接口JSON数据,封装为标准的HTTP响应报文,设置响应状态码、响应头、响应体,通过Web容器响应通道返回给前端客户端。

步骤9:资源释放与后置处理

请求响应完成后,SpringMVC执行后置操作:触发拦截器afterCompletion后置方法、销毁本次请求的临时参数资源、释放Tomcat工作线程、清理请求上下文,避免内存泄漏,保证服务高并发稳定性。

步骤10:单次请求流程彻底结束

客户端接收响应数据/页面,完成一次完整的SpringMVC请求交互,等待下一次请求触发。

💡 核心总结(面试必背) :SpringMVC工作流程核心就是 DispatcherServlet调度 + HandlerMapping路由 + HandlerAdapter执行 + 业务处理 + 视图/数据响应,所有通用底层操作全部由框架自动化实现,开发者仅聚焦业务开发,这也是SpringMVC高效、规范、适配企业级开发的核心原因。

三、SpringMVC环境搭建(Spring6 纯注解企业版)

摒弃传统繁琐的XML全局配置,基于Spring6 + 纯注解配置 + Maven搭建标准SpringMVC开发环境,适配前后端分离、传统Web项目双场景,是目前企业新项目的标准搭建方案。

3.1 全套Maven核心依赖(适配Spring6)

统一版本管控,引入SpringMVC核心、Web容器、JSON解析、单元测试全套依赖,兼容JDK17+、Jakarta EE新标准,无冗余依赖:

xml 复制代码
<properties>
    <spring.version>6.0.11</spring.version>
    <junit.version>4.13.2</junit.version>
    <jackson.version>2.15.2</jackson.version>
</properties>

<dependencies>
    <!-- Spring6核心容器依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- SpringMVC核心Web依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- Jakarta Web规范依赖(Spring6必备) -->
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>6.0.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>jakarta.servlet.jsp</groupId>
        <artifactId>jakarta.servlet.jsp-api</artifactId>
        <version>3.0.0</version>
        <scope>provided</scope>
    </dependency>

    <!-- JSON序列化解析依赖(前后端分离必备) -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
    </dependency>

    <!-- 单元测试依赖 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3.2 纯注解核心配置类(替代所有XML)

通过三组核心配置类,彻底消灭XML配置,分别实现Spring容器初始化、SpringMVC Web配置、Web容器全局配置,结构清晰、便于后期维护扩展。

3.2.1 Spring根容器配置类

负责扫描业务层、持久层组件,不扫描Web控制器组件,实现容器分层管理:

java 复制代码
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

// Spring根容器全局配置
@Configuration
// 扫描除Controller外的所有业务组件
@ComponentScan(value = "com.business",
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class))
public class SpringRootConfig {
}
3.2.2 SpringMVC Web配置类

开启MVC注解驱动、配置视图解析器、放行静态资源、注册拦截器、跨域配置,整合所有Web能力:

java 复制代码
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// SpringMVC Web配置类
@Configuration
@EnableWebMvc // 开启SpringMVC全注解驱动
@ComponentScan("com.business.controller") // 仅扫描控制器层
public class SpringMvcConfig implements WebMvcConfigurer {

    // 放行静态资源:css、js、img、html
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 匹配前端静态资源请求路径
        registry.addResourceHandler("/static/**")
                // 映射项目资源目录
                .addResourceLocations("classpath:/static/");
    }

    // 全局跨域配置
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*") // 允许所有域名跨域
                .allowedMethods("GET","POST","PUT","DELETE") // 允许所有请求方式
                .maxAge(3600); // 跨域缓存时长
    }
}
3.2.3 Web容器初始化配置类

替代web.xml,实现容器启动时自动加载Spring、SpringMVC配置,初始化前端控制器:

java 复制代码
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

// Web容器初始化配置,替代传统web.xml
public class WebAppInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {

    // 加载Spring根容器配置
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringRootConfig.class};
    }

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

    // 配置前端控制器拦截所有请求
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

四、SpringMVC核心请求处理与参数绑定

参数绑定是SpringMVC的核心能力,相较于原生Servlet手动获取参数、类型转换、封装对象的繁琐操作,SpringMVC实现了全自动参数解析、类型转换、数据封装,支持所有主流请求参数类型,适配企业全场景接口开发。

4.1 基础参数绑定(普通类型、字符串、日期)

自动绑定URL路径参数、表单提交参数,支持int、double、String、Date等基础类型,SpringMVC自动完成类型转换,无需手动处理。针对日期类型,提供全局格式化解决方案,彻底解决日期转换异常问题。

java 复制代码
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;

// 基础参数测试控制器
@RestController
@RequestMapping("/param/basic")
public class BasicParamController {

    /**
     * 普通基础类型参数绑定
     * @param userId 用户ID
     * @param userName 用户名
     * @param userScore 用户积分
     * @return 响应结果
     */
    @RequestMapping("/info")
    public String getBasicParam(Integer userId, String userName, Double userScore) {
        return "接收参数:用户ID=" + userId + ",用户名=" + userName + ",用户积分=" + userScore;
    }

    /**
     * 日期类型参数绑定(全局通用格式化)
     * @param createTime 创建时间
     * @return 响应结果
     */
    @RequestMapping("/date")
    public String getDateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date createTime) {
        return "接收日期参数:" + createTime;
    }
}

4.2 实体类参数绑定(表单自动封装)

支持直接将前端表单参数自动封装为Java实体类对象,要求表单参数名与实体类属性名一致,SpringMVC自动反射赋值,彻底省略手动set赋值操作,适配新增、修改表单场景。

4.2.1 自定义用户实体类
java 复制代码
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;

// 自定义用户实体类
public class User {
    private Integer userId;
    private String userName;
    private String userPhone;
    private Integer userAge;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date registerTime;

    // 无参构造
    public User() {}

    // 全参构造
    public User(Integer userId, String userName, String userPhone, Integer userAge, Date registerTime) {
        this.userId = userId;
        this.userName = userName;
        this.userPhone = userPhone;
        this.userAge = userAge;
        this.registerTime = registerTime;
    }

    // 完整getter、setter方法
    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPhone() {
        return userPhone;
    }

    public void setUserPhone(String userPhone) {
        this.userPhone = userPhone;
    }

    public Integer getUserAge() {
        return userAge;
    }

    public void setUserAge(Integer userAge) {
        this.userAge = userAge;
    }

    public Date getRegisterTime() {
        return registerTime;
    }

    public void setRegisterTime(Date registerTime) {
        this.registerTime = registerTime;
    }

    // 重写toString
    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userPhone='" + userPhone + '\'' +
                ", userAge=" + userAge +
                ", registerTime=" + registerTime +
                '}';
    }
}
4.2.2 实体类参数绑定控制器
java 复制代码
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/param/entity")
public class EntityParamController {

    /**
     * 自动封装前端表单参数为User实体对象
     * @param user 前端参数自动封装的实体对象
     * @return 实体对象信息
     */
    @RequestMapping("/user/add")
    public String addUser(User user) {
        // 直接使用封装后的实体对象,无需手动赋值
        return "自动封装用户信息:" + user.toString();
    }
}

4.3 JSON格式参数绑定(前后端分离核心)

前后端分离项目中,前端统一通过JSON格式传递参数,SpringMVC通过**@RequestBody**注解自动解析JSON字符串,封装为实体类、List、Map对象,是现代接口开发最常用的参数接收方式。

java 复制代码
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/param/json")
public class JsonParamController {

    /**
     * JSON对象参数绑定
     * @param user 解析后的用户实体对象
     * @return 响应结果
     */
    @PostMapping("/user/save")
    public String saveUserByJson(@RequestBody User user) {
        return "JSON解析用户信息:" + user;
    }

    /**
     * JSON数组参数绑定
     * @param userList 用户集合
     * @return 响应结果
     */
    @PostMapping("/user/batch")
    public String batchSaveUser(@RequestBody List<User> userList) {
        return "批量解析用户数量:" + userList.size() + ",数据:" + userList;
    }

    /**
     * JSON通用Map参数绑定(适配动态参数)
     * @param paramMap 动态参数集合
     * @return 响应结果
     */
    @PostMapping("/param/map")
    public String getDynamicParam(@RequestBody Map<String,Object> paramMap) {
        return "接收动态JSON参数:" + paramMap;
    }
}

4.4 路径变量参数绑定(RESTful风格)

适配RESTful接口设计规范,通过**@PathVariable**注解获取URL路径中的动态参数,替代传统问号传参,接口更简洁、规范,是企业微服务接口标准写法。

java 复制代码
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/rest")
public class RestParamController {

    /**
     * 单路径变量接收:根据用户ID查询用户
     * @param userId 用户ID
     * @return 响应结果
     */
    @RequestMapping("/user/{userId}")
    public String getUserById(@PathVariable Integer userId) {
        return "RESTful查询用户,ID:" + userId;
    }

    /**
     * 多路径变量接收:根据订单ID和状态查询订单
     * @param orderId 订单ID
     * @param orderStatus 订单状态
     * @return 响应结果
     */
    @RequestMapping("/order/{orderId}/{orderStatus}")
    public String getOrderInfo(@PathVariable String orderId, @PathVariable Integer orderStatus) {
        return "查询订单:订单号=" + orderId + ",订单状态=" + orderStatus;
    }
}

五、SpringMVC视图跳转与响应封装

SpringMVC提供两种核心响应模式:服务端视图跳转(传统Web项目)JSON数据响应(前后端分离项目),全面适配不同业务场景,同时支持重定向、请求转发、全局统一响应封装。

5.1 传统视图跳转(转发/重定向)

支持请求转发(服务器内部跳转,地址栏不变、携带参数)和重定向(客户端跳转,地址栏改变、不携带原请求参数),适配JSP、HTML视图页面渲染。

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/view")
public class ViewJumpController {

    /**
     * 普通请求转发(默认跳转方式)
     * @param model 模型对象,用于传递页面数据
     * @return 视图名称
     */
    @RequestMapping("/forward")
    public String forwardView(Model model) {
        // 向视图传递数据
        model.addAttribute("projectName","SpringMVC企业项目");
        model.addAttribute("author","Java进阶开发");
        // 转发到success.jsp页面
        return "success";
    }

    /**
     * 重定向跳转
     * @return 重定向路径
     */
    @RequestMapping("/redirect")
    public String redirectView() {
        // 重定向到指定接口/页面
        return "redirect:/view/forward";
    }
}

5.2 前后端分离JSON统一响应

企业前后端分离项目中,禁止直接返回零散数据,必须封装全局统一响应结果实体,统一返回格式、状态码、提示信息、业务数据,方便前端统一解析处理。

5.2.1 全局统一响应工具类
java 复制代码
// 全局统一接口响应实体
public class Result<T> {
    // 响应状态码:200成功,500失败
    private Integer code;
    // 响应提示信息
    private String msg;
    // 响应业务数据
    private T data;

    // 成功响应(带数据)
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMsg("请求成功");
        result.setData(data);
        return result;
    }

    // 成功响应(无数据)
    public static <T> Result<T> success() {
        return success(null);
    }

    // 失败响应
    public static <T> Result<T> error(Integer code, String msg) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    // getter、setter
    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
5.2.2 统一响应实战控制器
java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/response")
public class ResponseController {

    /**
     * 查询单个用户信息-统一响应
     */
    @GetMapping("/user/get")
    public Result<User> getUserInfo() {
        User user = new User(1001,"张三","13800138000",24,null);
        return Result.success(user);
    }

    /**
     * 查询用户列表-统一响应
     */
    @GetMapping("/user/list")
    public Result<List<User>> getUserList() {
        List<User> userList = new ArrayList<>();
        userList.add(new User(1001,"张三","13800138000",24,null));
        userList.add(new User(1002,"李四","13900139000",26,null));
        return Result.success(userList);
    }

    /**
     * 业务异常响应
     */
    @GetMapping("/user/error")
    public Result<Void> userError() {
        return Result.error(500,"用户信息查询失败,用户不存在");
    }
}

六、SpringMVC文件上传与下载(企业全场景)

文件上传下载是项目通用核心功能,SpringMVC封装了极简的文件操作API,支持单文件上传、多文件批量上传、异步无刷新上传、跨服务器上传、文件批量下载,适配头像上传、附件上传、资料导出等生产场景。

6.1 核心文件上传配置

在SpringMVC配置类中注册文件上传解析器,配置文件大小限制、临时缓存、编码格式,解决大文件上传、乱码问题:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// 新增文件上传解析器Bean
@Bean
public MultipartResolver multipartResolver() {
    return new StandardServletMultipartResolver();
}

6.2 单文件上传实战

接收前端文件流,自动封装为MultipartFile对象,通过UUID重命名文件避免文件名冲突,统一存储到服务器指定目录:

java 复制代码
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.UUID;

@RestController
@RequestMapping("/file")
public class FileUploadController {

    // 定义文件存储根目录
    private static final String UPLOAD_BASE_PATH = "D:/project_upload/springmvc_file/";

    /**
     * 单文件上传接口
     * @param file 前端上传的文件对象
     * @param request 请求对象
     * @return 上传结果
     */
    @PostMapping("/upload/single")
    public Result<String> singleFileUpload(MultipartFile file, HttpServletRequest request) {
        try {
            // 判断文件是否为空
            if (file.isEmpty()) {
                return Result.error(500,"上传文件不能为空");
            }
            // 获取原始文件名
            String originalFilename = file.getOriginalFilename();
            // 生成唯一文件名,避免覆盖
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            String newFileName = UUID.randomUUID().toString().replace("-","") + suffix;

            // 创建存储目录
            File dir = new File(UPLOAD_BASE_PATH);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            // 拼接完整文件路径
            File targetFile = new File(dir, newFileName);
            // 文件写入服务器磁盘
            file.transferTo(targetFile);

            // 返回文件访问路径
            String fileUrl = "/static/upload/" + newFileName;
            return Result.success(fileUrl);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(500,"文件上传失败:" + e.getMessage());
        }
    }
}

6.3 多文件批量上传实战

通过MultipartFile数组接收多文件,遍历批量保存,适配批量上传图片、批量导入附件等场景:

java 复制代码
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@PostMapping("/upload/batch")
public Result<List<String>> batchFileUpload(MultipartFile[] files) {
    List<String> fileUrlList = new ArrayList<>();
    try {
        // 遍历所有上传文件
        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                continue;
            }
            // 文件名处理
            String originalFilename = file.getOriginalFilename();
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            String newFileName = UUID.randomUUID().toString().replace("-","") + suffix;

            // 保存文件
            File dir = new File(UPLOAD_BASE_PATH);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File targetFile = new File(dir, newFileName);
            file.transferTo(targetFile);

            // 记录文件路径
            fileUrlList.add("/static/upload/" + newFileName);
        }
        return Result.success(fileUrlList);
    } catch (Exception e) {
        e.printStackTrace();
        return Result.error(500,"批量上传失败:" + e.getMessage());
    }
}

6.4 文件下载实战(浏览器直接下载)

读取服务器本地文件,通过响应流写入浏览器,设置响应头实现文件强制下载,支持图片、文档、压缩包等所有格式文件:

java 复制代码
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

@RestController
@RequestMapping("/file")
public class FileDownloadController {

    private static final String DOWNLOAD_BASE_PATH = "D:/project_upload/springmvc_file/";

    /**
     * 文件下载接口
     * @param fileName 文件名
     * @return 文件响应实体
     */
    @GetMapping("/download")
    public ResponseEntity<byte[]> fileDownload(@RequestParam String fileName) {
        try {
            // 拼接文件完整路径
            File file = new File(DOWNLOAD_BASE_PATH, fileName);
            if (!file.exists()) {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body("文件不存在".getBytes());
            }

            // 读取文件字节流
            byte[] bytes = new byte[(int) file.length()];
            FileInputStream fis = new FileInputStream(file);
            fis.read(bytes);
            fis.close();

            // 设置响应头,触发浏览器下载
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Disposition","attachment;filename=" + fileName);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

            // 返回文件字节数据
            return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件下载失败".getBytes());
        }
    }
}

七、SpringMVC全局异常处理(生产级方案)

传统开发中,控制器异常需要逐个捕获,代码冗余、异常返回格式不统一、前端无法统一处理。SpringMVC提供全局异常处理器,基于AOP思想统一拦截所有控制器异常,实现异常统一捕获、统一封装、友好提示,是生产项目必备优化方案。

7.1 自定义业务异常类

自定义业务异常,区分系统异常和手动抛出的业务异常,精准返回异常状态码和提示信息:

java 复制代码
// 自定义业务异常
public class BusinessException extends RuntimeException {
    private Integer code;
    private String msg;

    public BusinessException(Integer code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

7.2 全局统一异常处理器

通过@ControllerAdvice声明全局异常类,拦截所有控制器异常,区分自定义业务异常、空指针异常、算术异常、通用系统异常,分类处理、统一返回格式:

java 复制代码
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

// 全局异常处理器,拦截所有控制器异常
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    /**
     * 处理自定义业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
        return Result.error(e.getCode(), e.getMsg());
    }

    /**
     * 处理空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<Void> handleNullPointException() {
        return Result.error(500,"系统异常:数据为空,操作失败");
    }

    /**
     * 处理算术运算异常
     */
    @ExceptionHandler(ArithmeticException.class)
    public Result<Void> handleArithmeticException() {
        return Result.error(500,"系统异常:运算错误");
    }

    /**
     * 处理所有未知通用异常
     */
    @ExceptionHandler(Exception.class)
    public Result<Void> handleAllException(Exception e) {
        e.printStackTrace();
        return Result.error(500,"服务器繁忙,请稍后重试");
    }
}

7.3 异常测试控制器

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

@RestController
@RequestMapping("/exception")
public class ExceptionTestController {

    // 测试自定义业务异常
    @GetMapping("/business")
    public Result<Void> testBusinessException() {
        throw new BusinessException(4001,"用户权限不足,无法操作");
    }

    // 测试空指针异常
    @GetMapping("/null")
    public Result<Void> testNullException() {
        String str = null;
        str.length();
        return Result.success();
    }

    // 测试算术异常
    @GetMapping("/math")
    public Result<Void> testMathException() {
        int num = 1 / 0;
        return Result.success();
    }
}

八、SpringMVC拦截器(Interceptor)实战精讲

SpringMVC拦截器是基于AOP思想的请求增强组件,专门用于拦截控制器请求,实现登录校验、权限拦截、日志记录、敏感词过滤、请求限流等通用功能,无侵入业务代码,是企业权限管控的核心组件。

拦截器与过滤器核心区别(面试重点):拦截器是SpringMVC组件、仅拦截控制器请求、可获取Spring容器Bean、不依赖Web容器;过滤器是Servlet组件、拦截所有请求(静态资源+控制器)、无法直接获取Spring Bean、依赖Web容器。

8.1 拦截器核心三大方法

自定义拦截器需实现HandlerInterceptor接口,包含三个核心方法,对应请求全生命周期:

  • preHandle:控制器方法执行前执行,返回true放行、false拦截,用于权限校验、登录验证;

  • postHandle:控制器方法执行完毕、视图渲染前执行,用于修改模型数据、响应数据;

  • afterCompletion:视图渲染完成、请求结束后执行,用于资源释放、请求日志记录。

8.2 登录权限拦截器实战

实现企业通用的登录拦截功能:未登录用户禁止访问业务接口,已登录用户正常放行,放行登录、注册、静态资源接口:

8.2.1 自定义登录拦截器
java 复制代码
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginInterceptor implements HandlerInterceptor {

    // 请求前置拦截:登录权限校验
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取请求地址
        String requestUri = request.getRequestURI();
        // 放行登录、注册接口
        if (requestUri.contains("/user/login") || requestUri.contains("/user/register") || requestUri.contains("/static/")) {
            return true;
        }

        // 从session获取登录用户信息
        Object loginUser = request.getSession().getAttribute("loginUser");
        // 未登录则拦截请求,返回提示信息
        if (loginUser == null) {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"msg\":\"用户未登录,请先登录\"}");
            return false;
        }
        // 已登录放行
        return true;
    }

    // 控制器执行后、视图渲染前
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    // 请求完全结束后
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}
8.2.2 注册拦截器到Spring容器

在SpringMVC配置类中注册拦截器,配置拦截路径与放行路径:

java 复制代码
// 重写拦截器注册方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoginInterceptor())
            .addPathPatterns("/**") // 拦截所有请求
            .excludePathPatterns("/user/login","/user/register","/static/**"); // 放行指定请求
}

8.3 多拦截器执行顺序规则

项目中可配置多个拦截器形成拦截器链,执行顺序遵循核心规则:preHandle顺序执行,postHandle、afterCompletion逆序执行;任意拦截器preHandle返回false,后续所有拦截器和控制器方法均不执行,已执行的preHandle对应的afterCompletion会正常执行。

九、SpringMVC跨域请求解决方案

浏览器同源策略会限制跨域请求:协议、域名、端口任意一个不同,即为跨域,前端Ajax请求会被浏览器拦截。SpringMVC提供三种企业级跨域解决方案,适配不同开发场景。

9.1 注解式跨域(单接口生效)

通过@CrossOrigin注解作用于控制器类/方法,精准控制单个接口跨域,适合少量接口需要跨域的场景:

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

@RestController
@RequestMapping("/cors")
// 当前类所有接口允许跨域
@CrossOrigin(origins = "*",maxAge = 3600)
public class CorsController {

    @GetMapping("/test")
    public Result<String> corsTest() {
        return Result.success("跨域请求成功");
    }
}

9.2 全局跨域配置(全项目生效)

前文已配置全局跨域,作用于项目所有接口,适合前后端分离项目统一跨域处理,无需逐个接口配置,是企业主流方案。

9.3 过滤器跨域配置(兼容老旧项目)

通过自定义跨域过滤器实现全局跨域,兼容低版本Spring项目:

java 复制代码
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        // 设置跨域响应头
        response.setHeader("Access-Control-Allow-Origin","*");
        response.setHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS");
        response.setHeader("Access-Control-Allow-Headers","*");
        response.setHeader("Access-Control-Max-Age","3600");

        // 放行OPTIONS预请求
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(200);
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

十、SSM整合企业完整实战(Spring6+MyBatis3.5+SpringMVC)

整合Spring6+SpringMVC+MyBatis3.5 三大核心框架,搭建标准企业级SSM架构,实现数据库CRUD、事务管控、请求处理、视图渲染、异常拦截、权限校验全套能力,完全贴合生产项目架构规范。

整合核心思路:Spring容器管理所有业务Bean(Service、Dao、事务)、MyBatis负责数据库持久化操作、SpringMVC负责Web请求调度与响应,三层架构完全解耦。

10.1 项目分层架构

标准企业分层结构,职责清晰、规范统一:

  • entity实体层:封装数据库表对应实体类

  • dao持久层:数据库CRUD接口,MyBatis实现数据访问

  • service业务层:封装业务逻辑,Spring声明式事务管控

  • controller控制层:接收前端请求、调用业务层、封装响应数据

  • config配置层:所有框架注解配置类

  • common公共层:统一响应、全局异常、工具类

10.2 数据库初始化脚本

sql 复制代码
CREATE DATABASE IF NOT EXISTS ssm_enterprise DEFAULT CHARACTER SET utf8mb4;
USE ssm_enterprise;

-- 员工业务表
CREATE TABLE emp (
    emp_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID',
    emp_name VARCHAR(30) NOT NULL COMMENT '员工姓名',
    emp_gender VARCHAR(10) COMMENT '员工性别',
    emp_salary DECIMAL(10,2) COMMENT '员工薪资',
    emp_dept VARCHAR(30) COMMENT '所属部门',
    create_time DATETIME DEFAULT NOW() COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工信息表';

-- 初始化测试数据
INSERT INTO emp(emp_name,emp_gender,emp_salary,emp_dept) 
VALUES ('张三','男',8500.00,'研发部'),
('李四','女',7200.00,'测试部'),
('王五','男',9800.00,'架构部');

10.3 完整业务代码实现

10.3.1 员工实体类
java 复制代码
import java.util.Date;

public class Emp {
    private Integer empId;
    private String empName;
    private String empGender;
    private Double empSalary;
    private String empDept;
    private Date createTime;

    // 无参、全参构造、getter、setter、toString
    public Emp() {}

    public Emp(Integer empId, String empName, String empGender, Double empSalary, String empDept, Date createTime) {
        this.empId = empId;
        this.empName = empName;
        this.empGender = empGender;
        this.empSalary = empSalary;
        this.empDept = empDept;
        this.createTime = createTime;
    }

    // 省略getter/setter
}
10.3.2 Dao持久层接口
java 复制代码
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface EmpDao {

    // 查询所有员工
    @Select("select * from emp")
    List<Emp> findAllEmp();

    // 根据ID查询员工
    @Select("select * from emp where emp_id = #{empId}")
    Emp findEmpById(Integer empId);

    // 新增员工
    @Insert("insert into emp(emp_name,emp_gender,emp_salary,emp_dept) values(#{empName},#{empGender},#{empSalary},#{empDept})")
    int addEmp(Emp emp);

    // 修改员工
    @Update("update emp set emp_name=#{empName},emp_gender=#{empGender},emp_salary=#{empSalary},emp_dept=#{empDept} where emp_id=#{empId}")
    int updateEmp(Emp emp);

    // 删除员工
    @Delete("delete from emp where emp_id = #{empId}")
    int deleteEmp(Integer empId);
}
10.3.3 Service业务层接口与实现类

业务层封装CRUD业务逻辑,添加Spring声明式事务,保证数据库操作原子性:

java 复制代码
import java.util.List;

// 业务接口
public interface EmpService {
    // 查询全部员工
    List<Emp> findAllEmp();
    // 根据ID查询员工
    Emp findEmpById(Integer empId);
    // 新增员工
    int addEmp(Emp emp);
    // 修改员工
    int updateEmp(Emp emp);
    // 删除员工
    int deleteEmp(Integer empId);
}
java 复制代码
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;

@Service
public class EmpServiceImpl implements EmpService {

    @Resource
    private EmpDao empDao;

    @Override
    public List<Emp> findAllEmp() {
        return empDao.findAllEmp();
    }

    @Override
    public Emp findEmpById(Integer empId) {
        return empDao.findEmpById(empId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addEmp(Emp emp) {
        return empDao.addEmp(emp);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateEmp(Emp emp) {
        return empDao.updateEmp(emp);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteEmp(Integer empId) {
        return empDao.deleteEmp(empId);
    }
}
10.3.4 Controller控制层接口(前后端分离)

编写全套员工CRUD接口,使用全局统一响应格式,适配前后端分离开发:

java 复制代码
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/emp")
public class EmpController {

    @Resource
    private EmpService empService;

    /**
     * 查询所有员工
     */
    @GetMapping("/list")
    public Result<List<Emp>> getEmpList(){
        List<Emp> empList = empService.findAllEmp();
        return Result.success(empList);
    }

    /**
     * 根据ID查询员工
     */
    @GetMapping("/{empId}")
    public Result<Emp> getEmpById(@PathVariable Integer empId){
        Emp emp = empService.findEmpById(empId);
        return Result.success(emp);
    }

    /**
     * 新增员工
     */
    @PostMapping("/add")
    public Result<Void> addEmp(@RequestBody Emp emp){
        int rows = empService.addEmp(emp);
        if(rows > 0){
            return Result.success();
        }
        return Result.error(500,"新增员工失败");
    }

    /**
     * 修改员工
     */
    @PutMapping("/update")
    public Result<Void> updateEmp(@RequestBody Emp emp){
        int rows = empService.updateEmp(emp);
        if(rows > 0){
            return Result.success();
        }
        return Result.error(500,"修改员工失败");
    }

    /**
     * 删除员工
     */
    @DeleteMapping("/delete/{empId}")
    public Result<Void> deleteEmp(@PathVariable Integer empId){
        int rows = empService.deleteEmp(empId);
        if(rows > 0){
            return Result.success();
        }
        return Result.error(500,"删除员工失败");
    }
}

10.4 SSM全套核心配置类(纯注解、无XML)

SSM整合核心在于三大容器配置解耦整合,补齐MyBatis、事务、数据源完整配置,适配Spring6新标准。

10.4.1 数据源与MyBatis配置类
java 复制代码
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.IOException;

@Configuration
@PropertySource("classpath:db.properties") // 加载数据库配置文件
@MapperScan("com.business.dao") // 扫描Dao接口
@EnableTransactionManagement // 开启事务管理
public class MyBatisConfig {

    @Resource
    private Environment env;

    // 数据源Bean
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("db.driver"));
        dataSource.setUrl(env.getProperty("db.url"));
        dataSource.setUsername(env.getProperty("db.username"));
        dataSource.setPassword(env.getProperty("db.password"));
        return dataSource;
    }

    // SqlSession工厂Bean
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        // 设置数据源
        factoryBean.setDataSource(dataSource);
        // 设置实体类别名包
        factoryBean.setTypeAliasesPackage("com.business.entity");
        // 加载Mapper映射文件
        Resource[] resources = new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/*.xml");
        factoryBean.setMapperLocations(resources);
        return factoryBean;
    }
}
10.4.2 数据库配置文件 db.properties
properties 复制代码
# MySQL8.0+ 驱动配置(适配Spring6、JDK17)
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/ssm_enterprise?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
db.username=root
db.password=123456

10.5 统一整合配置说明

至此,Spring6 + SpringMVC + MyBatis3.5 企业级SSM架构完全整合完成,整体整合逻辑闭环:

1、容器分层

Spring根容器:管理Service、DataSource、事务、MyBatis核心Bean,不扫描Web控制器;

SpringMVC子容器:仅管理Controller、拦截器、Web视图、文件解析等Web组件,父子容器隔离,解耦彻底。

2、全链路闭环

前端请求 → DispatcherServlet → 路由匹配 → 参数绑定 → Controller → Service(事务)→ Dao → MyBatis → MySQL数据库,完美实现完整Web业务链路。

3、生产级特性全覆盖

包含:纯注解零XML配置、全局统一响应、全局异常拦截、事务控制、参数自动绑定、跨域处理、文件上传下载、拦截器权限校验,完全适配企业项目上线标准。

💡 本章核心总结 :SSM整合的本质是Spring管业务与事务、MyBatis管数据持久化、SpringMVC管Web请求调度,三者各司其职,是Java后端最经典、最通用、面试必问的企业级基础架构,也是SpringBoot底层核心雏形。

相关推荐
ㄣ知冷煖★3 小时前
统一网关架构实践:从 Token 鉴权到路由、策略与凭证池转发全链路解析
java·服务器·架构
Lumbrologist3 小时前
【C++】零基础入门 · 第 2 节:变量、基本数据类型与输入输出
java·开发语言·c++
GISer_Jing3 小时前
Three.JS渲染架构解读
java·javascript·架构
绝知此事3 小时前
Netty实战:从零构建高性能TCP通信服务(含心跳检测)
java·网络·spring boot·网络协议·tcp/ip
码完就睡3 小时前
C语言——动态内存
c语言·开发语言
发现一只大呆瓜3 小时前
超全 Vite 性能优化指南:网络、资源、预渲染三维落地方案
前端·面试·vite
Dicky-_-zhang3 小时前
分布式事务解决方案TCC实战
java·jvm
xyq20244 小时前
Java 数组
开发语言
雨辰AI4 小时前
人大金仓 V9 生产级专用监控大盘(含 120 + 指标 + 告警规则 + 一键导入)
java·开发语言·数据库·mysql·政务