Spring Boot 整合 Spring MVC:自动配置与扩展实践

Spring MVC 作为 Java Web 开发的核心框架,在传统 SSM 项目中需要大量 XML 配置(如 DispatcherServlet、视图解析器等)。而 Spring Boot 通过 "自动配置" 特性,简化了 Spring MVC 的整合过程,同时保留了灵活的扩展能力。本文将从自动配置原理、扩展方式、组件注册等方面,结合实例详解 Spring Boot 与 Spring MVC 的整合实践。

一、Spring Boot 对 Spring MVC 的自动配置

Spring Boot 的核心优势之一是 "约定大于配置",对于 Spring MVC 的核心组件,Spring Boot 已完成自动配置,无需手动编写 XML 或 Java 配置。

1.1 核心组件的自动配置

(1)DispatcherServlet(中央转发器)

传统 SSM 中需在web.xml配置 DispatcherServlet:

<servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>

Spring Boot 通过DispatcherServletAutoConfiguration类自动注册 DispatcherServlet,默认拦截路径为/(不拦截 JSP),且无需web.xml(因为 Spring Boot 以 JAR 包方式运行嵌入式容器)。

(2)控制器(Controller)

只需在类上标注@Controller@RestController,并确保类在 Spring Boot 的注解扫描范围内(默认扫描主启动类所在包及其子包),Spring Boot 会自动将其注册为 Bean。

(3)视图解析器

传统 SSM 需配置 InternalResourceViewResolver:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/>
</bean>
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));resolver.setOrder(-2147483648);return resolver;
}
当我们做文件上传的时候我们也会发现multipartResolver是自动被配置好的
页面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data"><input name="pic" type="file"><input type="submit">
</form>
</body>
</html>
Controller
package com.qcby.mavenspringboot.controller;import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.slf4j.LoggerFactory;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import org.slf4j.Logger;@Controller
public class UploadController {private static final Logger logger = LoggerFactory.getLogger(UploadController.class);@RequestMapping(value = "/upload", method = RequestMethod.GET)public String showUploadForm() {return "upload";}// 2. 处理POST请求:接收文件上传@RequestMapping(value = "/upload", method = RequestMethod.POST)public String upload(@RequestParam("pic") MultipartFile file,HttpServletRequest request,Model model) {if (file.isEmpty()) {logger.error("上传的文件为空");return "upload";}String originalFileName = file.getOriginalFilename();String contentType = file.getContentType();logger.info("原始文件名: {}", originalFileName);logger.info("文件类型: {}", contentType);String uniqueFileName = generateUniqueFileName(originalFileName);// 本地存储路径(D:/imgup/)Path filePath = Paths.get("D:", "imgup");try {// 上传文件到本地路径uploadFile(file.getBytes(), filePath, uniqueFileName);String fileAccessUrl = "/webimg/" + uniqueFileName;model.addAttribute("originalFileName", originalFileName);model.addAttribute("fileUrl", fileAccessUrl);model.addAttribute("fileType", contentType);} catch (IOException e) {logger.error("文件上传失败", e);return "error";}return "success";}private static void uploadFile(byte[] file, Path filePath, String fileName) throws IOException {// 若目录不存在则创建if (!Files.exists(filePath)) {Files.createDirectories(filePath);}Path targetPath = filePath.resolve(fileName);// 写入文件(try-with-resources自动关闭流)try (FileOutputStream out = new FileOutputStream(targetPath.toFile())) {out.write(file);}}// 生成唯一文件名(UUID+原文件后缀)private static String generateUniqueFileName(String originalFileName) {String extension = "";int dotIndex = originalFileName.lastIndexOf('.');if (dotIndex > 0) {extension = originalFileName.substring(dotIndex);}return UUID.randomUUID().toString() + extension;}
}

 上传文件展示代码:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"> <!-- 引入Thymeleaf命名空间 -->
<head><meta charset="UTF-8"><title>上传成功</title>
</head>
<body>
<h1>文件上传成功!</h1>
<p>原始文件名:<span th:text="${fileName}"></span></p><div><h3>预览图片:</h3><img th:src="${fileUrl}" style="max-width: 500px; max-height: 500px;" />
</div><!--<div>-->
<!--    <a th:href="${fileUrl}" th:text="下载文件:+${fileName}"></a>-->
<!--</div>-->
</body>
</html>

 

Spring Boot 自动注册ContentNegotiatingViewResolver(组合所有视图解析器)和BeanNameViewResolverContentNegotiatingViewResolver会根据请求头(如Accept)动态选择合适的视图解析器,无需手动配置前缀 / 后缀(如需自定义,可通过扩展方式实现)。

(4)文件上传(MultipartResolver)

Spring Boot 自动配置StandardServletMultipartResolver,支持文件上传。前端表单需指定enctype="multipart/form-data",后端通过@RequestParam("file") MultipartFile file接收文件。

默认上传大小限制为 10MB,可在application.properties中修改:

spring.servlet.multipart.max-file-size=100MB  # 单个文件大小
spring.servlet.multipart.max-request-size=500MB  # 总请求大小

 

(5)静态资源访问

Spring Boot 默认将classpath:/static/classpath:/public/classpath:/resources/classpath:/META-INF/resources/目录下的资源视为静态资源,可直接通过 URL 访问(如http://localhost:8080/xxx.js)。

(6)消息转换器与格式化
  • 消息转换器:自动配置HttpMessageConverter,支持 JSON(默认 Jackson)、XML 等数据格式的序列化 / 反序列化。
  • 格式化:默认支持日期、数字等类型的格式化,可通过application.properties配置全局日期格式:
    spring.mvc.format.date=yyyy-MM-dd
    

 

格式化转换器的自动注册

时间类型我们可以在这里修改

在配置文件中指定好时间的模式我们就可以输入了

(7)欢迎页

默认将classpath:/static/index.htmlclasspath:/templates/index.html(Thymeleaf)作为欢迎页,访问http://localhost:8080/时自动跳转。

 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>

 

二、扩展 Spring MVC:通过 WebMvcConfigurer

Spring Boot 的自动配置并非 "一刀切",实际开发中常需自定义配置(如拦截器、消息转换器)。通过实现WebMvcConfigurer接口(推荐),可在不覆盖自动配置的前提下扩展 Spring MVC。

2.1 核心扩展场景与示例

创建配置类MyMVCCofnig实现WebMvcConfigurer,并重写对应方法:

@Configuration
public class MyMVCCofnig implements WebMvcConfigurer {// 扩展方法...
}
(1)视图控制器(请求转发)

通过addViewControllers实现 URL 与视图的直接映射(无需编写 Controller):

@Override
public void addViewControllers(ViewControllerRegistry registry) {// 访问http://localhost:8080/tx时,转发到success.htmlregistry.addViewController("/tx").setViewName("success");
}

success页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>Success</h1>
</body>
</html>

 

(2)自定义格式化器

通过addFormatters注册自定义格式化器(如日期解析):

@Override
public void addFormatters(FormatterRegistry registry) {// 自定义日期格式化(将"yyyy-MM-dd"字符串转为Date)registry.addFormatter(new Formatter<Date>() {@Overridepublic String print(Date date, Locale locale) {return null; // 响应时格式化(按需实现)}@Overridepublic Date parse(String s, Locale locale) throws ParseException {return new SimpleDateFormat("yyyy-MM-dd").parse(s);}});
}

测试:控制器接口接收 Date 参数:

@GetMapping("/testDate")
public String testDateFormatter(@RequestParam("date") Date date) {return "日期:" + date.toLocaleString();
}
// 访问:http://localhost:8080/testDate?date=2023-10-01 可正常解析

 

(3)整合 FastJSON 消息转换器

默认 Jackson 可能无法满足需求(如 JSON 格式化),可替换为 FastJSON:

  1. 引入依赖:
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version>
</dependency>
  1. 注册 FastJSON 转换器:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();FastJsonConfig config = new FastJsonConfig();// 配置JSON格式化(如PrettyFormat)config.setSerializerFeatures(SerializerFeature.PrettyFormat);converter.setFastJsonConfig(config);converters.add(converter);
}
  1. 实体类自定义字段格式:
public class User {@JSONField(format = "yyyy-MM-dd HH:mm:ss") // 序列化日期格式private Date date;// 其他字段...
}

测试:接口返回 User 对象时,日期将按指定格式序列化:

@GetMapping("/user")
public User getUser() {User user = new User("张三", "123", 20, 90.5, 1);user.setDate(new Date());return user;
}

 

(4)注册拦截器

拦截器可用于登录验证、日志记录等场景,需两步:

  1. 创建自定义拦截器:
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {System.out.println("前置拦截(请求处理前)");return true; // true放行,false拦截}@Overridepublic void postHandle(...) {System.out.println("后置拦截(视图渲染前)");}@Overridepublic void afterCompletion(...) {System.out.println("最终拦截(请求完成后)");}
}
  1. 通过addInterceptors注册拦截器:
@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**") // 拦截所有请求.excludePathPatterns("/hello2"); // 排除/hello2请求
}

测试:访问/hello会被拦截,访问/hello2不会:

@GetMapping("/hello")
public String hello() {System.out.println("执行/hello业务逻辑");return "hello";
}
@GetMapping("/hello2")
public String hello2() {return "hello2";
}

 

 

三、注册 Servlet 三大组件(Servlet、Filter、Listener)

传统 Web 项目通过web.xml注册 Servlet、Filter、Listener,而 Spring Boot 需通过@Bean手动注册(因无 web.xml)。

3.1 注册示例(配置类 ServletConfig)

@Configuration
public class ServletConfig {// 1. 注册Servlet@Beanpublic ServletRegistrationBean<MyServlet> myServlet() {// 参数1:自定义Servlet实例;参数2:访问路径ServletRegistrationBean<MyServlet> registrationBean = new ServletRegistrationBean<>(new MyServlet(), "/myServlet");registrationBean.setLoadOnStartup(1); // 启动时加载(优先级)return registrationBean;}// 2. 注册Filter@Beanpublic FilterRegistrationBean<MyFilter> myFilter() {FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new MyFilter()); // 自定义FilterregistrationBean.setUrlPatterns(Arrays.asList("/hello", "/myServlet")); // 拦截路径return registrationBean;}// 3. 注册Listener@Beanpublic ServletListenerRegistrationBean<MyListener> myListener() {ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());return registrationBean;}
}

3.2 自定义组件实现

(1)自定义 Servlet
public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {resp.getWriter().write("Hello MyServlet");}
}

访问http://localhost:8080/myServlet会执行该 Servlet。

 

(2)自定义 Filter
public class MyFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("Filter拦截请求");chain.doFilter(request, response); // 放行System.out.println("Filter处理完成");}
}
(3)自定义 Listener
public class MyListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("Listener:应用启动初始化");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("Listener:应用关闭销毁");}
}

 

四、项目依赖与配置

4.1 核心依赖(pom.xml)

<dependencies><!-- Web启动器(包含Spring MVC、嵌入式Tomcat等) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- FastJSON(消息转换) --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><!-- Thymeleaf(模板引擎,可选) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- 热部署(开发环境) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency>
</dependencies>

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

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

相关文章

print(“\033[31m红\033[32m绿\033[34m蓝\033[0m默认色“)

可以让python的终端字体有着不一样的颜色。代码&#xff1a;print("\033[31m红\033[32m绿\033[34m蓝\033[0m默认色")效果&#xff1a;

LNMP-zblog分布式部署

一、准备3台主机&#xff08;rocky8&#xff09;下载相应服务[rootnginx ~]# yum install -y nginx nfs-utils[rootphp ~]# yum install -y nfs-utils php-mysqlnd php php-fpm[rootmysql ~]# yum install -y mysql-server二、挂载php端[rootphp ~]# vim /etc/exports [rootphp…

常见代码八股

1. 利用梯度下降法&#xff0c;计算二次函数yx^2x4的最小值 def target_function(x):return x ** 2 x 4def gradient(x):return 2*x 1x_init 10 x x_init steps 100 lr 0.1 for i in range(100):x x - lr*gradient(x)print(f"最小值 f(x) {target_function(x):.4f…

【深入底层】C++开发简历4+4技能描述6

简历书写 熟悉C的封装、继承、多态&#xff0c;STL常用容器&#xff0c;熟悉C11的Lambda表达式、智能指针等&#xff0c;熟悉C20协程语法&#xff0c;具有良好的编码习惯与文档能力。 回答思路 这里是基本上就是要全会&#xff0c;考察的问题也很固定&#xff0c;stl这块可以定…

forest远程调用注意事项

1、如果在项目中&#xff0c;同时依赖了其中多个框架&#xff0c;那么按 Fastjson2 > Fastjson > Jackson > Gson 这样的优先级来判断&#xff0c;Forest 会以优先级最高的框架作为 JSON 转换器。2、Forest 支持哪几种 JSON 框架&#xff1f;A: 支持 Jackson、Gson、F…

网络资源模板--基于Android Studio 实现的新闻App

目录 一、测试环境说明 二、项目简介 三、项目演示 四、部设计详情&#xff08;部分) 登录页 首页 五、项目源码 一、测试环境说明 电脑环境 Windows 11 编写语言 JAVA 开发软件 Android Studio (2020) 开发软件只要大于等于测试版本即可(近几年官网直接下载也可…

通过Location API精准获取位置信息并优化定位精度!

&#x1f44b; 你好&#xff0c;欢迎来到我的博客&#xff01;我是【菜鸟不学编程】    我是一个正在奋斗中的职场码农&#xff0c;步入职场多年&#xff0c;正在从“小码农”慢慢成长为有深度、有思考的技术人。在这条不断进阶的路上&#xff0c;我决定记录下自己的学习与成…

构建可扩展的状态系统:基于 ArkTS 的模块化状态管理设计与实现

摘要 在 HarmonyOS 的日常开发中&#xff0c;很多人都会遇到一个问题&#xff1a;多个页面之间的数据状态如何共享&#xff1f;尤其是在组件结构越来越复杂的场景下&#xff0c;如果还用传统方式来传值&#xff0c;不仅代码混乱&#xff0c;维护也很吃力。 为了解决这个问题&am…

重生之我在暑假学习微服务第二天《MybatisPlus-下篇》

本系列参考黑马程序员微服务课程&#xff0c;有兴趣的可以去查看相关视频&#xff0c;本系列内容采用渐进式方式讲解微服务核心概念与实践方法&#xff0c;每日更新确保知识点的连贯性。通过系统化学习路径帮助开发者掌握分布式系统构建的关键技术。读者可通过平台订阅功能获取…

系统整理Python的条件语句和常用方法

Python 的条件语句&#xff08;if 语句&#xff09;是控制程序流程的基础之一&#xff0c;结构清晰、语法简洁&#xff0c;非常适合初学者掌握。一、基本语法结构if 条件:执行代码块1 elif 条件2:执行代码块2 else:执行代码块3示例&#xff1a;score 85if score > 90:print…

记录个IAR程序下载后硬件复位不运行,必须断电复位才运行的问题

【问题测试】有个F407的跑马灯的例子&#xff0c;是MDK和IAR两个版本&#xff0c;MDK版本的例子下载并复位后可以正常看到LED闪烁&#xff0c;而IAR的例子下进去后&#xff0c;不会闪烁。使用TOOL的上位机内核寄存器监测工具测试发现&#xff0c;硬件复位后竟然还在调试状态&am…

观察者模式(Observer Pattern)和 发布-订阅模式(Publisher-Subscriber Pattern)

你对 观察者模式&#xff08;Observer Pattern&#xff09;和 发布-订阅模式&#xff08;Publisher-Subscriber Pattern&#xff09;的描述是非常准确的&#xff0c;并且阐明了它们的核心区别。为了帮助你更好地理解这两者的细微差异&#xff0c;下面是一个更详细的对比分析&am…

2025年接口技术的十字路口:当MCP遇见REST、GraphQL与gRPC

在当今这个由数据驱动、万物互联的时代&#xff0c;应用程序接口&#xff08;API&#xff09;已成为现代软件架构的基石。它们是不同服务之间沟通的桥梁&#xff0c;支撑着从网页应用到复杂的微服务生态系统的一切。长久以来&#xff0c;开发者们在REST、GraphQL和gRPC这几种主…

【CTF-WEB-反序列化】利用__toString魔术方法读取flag.php

题目 页面提示输入?code&#xff0c;那我们在网址里get一下出现了新页面的提示&#xff0c;进入看看下面有个help.php页面的提示&#xff0c;进入看看有一段php代码&#xff0c;仔细分析&#xff0c;应该是要用反序列法代码如下 class FileClass{ public $filename error.log…

在 github.com 与 sourceforge.net 上创建免费个人静态网站、博客的区别

github.com github 属于 git 版本管理专业网站&#xff0c;有免费和收费两种套餐。git 的数据是存放在数据库中的&#xff0c;要将数据库中的数据显示为网站的网页&#xff0c;这需要服务器端提供专门的中间件支持才能实现。 特点&#xff1a; 官方支持&#xff1a;提供长期…

jenkins 入门指南:从安装到启动的完整教程

jenkins 入门指南&#xff1a;从安装到启动的完整教程 持续集成&#xff08;CI&#xff09;是现代开发流程中的核心环节&#xff0c;而 Jenkins 作为一款开源的 CI 工具&#xff0c;凭借简单安装、开箱即用、插件丰富、易于扩展等优势&#xff0c;成为开发者的首选工具。它能自…

机器学习(重学版)基础篇(概念与评估)

本篇参考周志华老师的西瓜书&#xff0c;但是本人学识有限仅能理解皮毛&#xff0c;如有错误诚请读友评论区指正&#xff0c;万分感谢。一、基础概念与评估方法本节目标&#xff1a;建立理论基础框架​1、机器学习定义机器学习是一门通过计算手段利用经验&#xff08;以数据形式…

spring/springboot SPI(二)配合使用的接口

spring.factories 里&#xff0c;Spring 会根据接口来加载实现类&#xff0c;常见的几个接口包括&#xff1a;一、org.springframework.context.ApplicationListener1、作用监听 Spring 容器事件&#xff0c;如 ApplicationReadyEvent。2、使用方法项目结构Spring Boot 2.xSpri…

基于Zig语言,opencv相关的c++程序静态交叉编译

一、写在前面 1.什么是zig? Zig 是一种通用编程语言&#xff1b; Zig 最初的定位就是代替C语言的系统级语言&#xff0c;它提供了与 C 语言几乎 100% 兼容&#xff08;可直接调用 C 头文件、链接 C 库&#xff09;&#xff0c;同时不需要任何依赖构建系统。 Zig 同时附带一…

基于 LSTM 与 SVM 融合的时间序列预测模型:理论框架与协同机制—实践算法(1)

目录 1、单一模型的局限性&#xff1a;混合架构的设计动机 2、LSTM 的时序特征提取&#xff1a;从原始序列到高阶表征 2.1、门控机制的时序过滤能力 2.2、隐藏状态的特征压缩作用 2.3、预训练的特征优化逻辑 3、SVM 的非线性映射&#xff1a;从高阶特征到预测输出 3.1、…