web场景
1.自动配置
1.整合web场景
2.引入 autoconfigure 功能


WebMvcConfigurer接口

1.参数解析器是什么,为什么要解析请求传进来的参数跟controller对应?
参数解析器 HandlerMethodArgumentResolver 一句话:SpringMVC 的工具,负责把前端发来的请求数据(url 参数、json 请求体),转换成 Controller 方法上的 Java 参数对象。
原理: 前端发请求,数据是字符串 /json 流。但是 Controller 方法写的是 User user、Integer id 这种 Java 类型。 Spring 不知道怎么把 http 请求里的数据,塞到你方法的参数上,参数解析器就是干这个映射转换的。
@RequestBody 就是由对应的参数解析器,读取 json 请求体,转成 User 对象
为什么要做? HTTP 传输只有字符串,Java 是强类型。必须有解析器做转换、绑定,否则你 Controller 拿不到参数。 你还可以自定义参数解析器,比如自动把 token 转换成当前登录用户对象。
2.跨域是什么意思?
跨域 = 浏览器的同源安全策略限制 同源:协议 + 域名 + 端口号,三个全部一样才叫同源。只要有一个不一样,就是跨域。
例子: 前端页面 http://localhost:8080,去请求后端接口 http://localhost:8888 端口不一样 → 跨域。 浏览器会拦截这个响应,不让前端拿到返回数据(请求其实已经发给后端了,是浏览器拦住返回结果)。
为什么会有跨域:浏览器安全机制,防止恶意网站偷取别的网站 cookie、数据。 解决方式:后端配置 CORS(就是 WebMvcConfigurer 里面
addCorsMappings),告诉浏览器允许这个前端域名访问。
⚠️ 重点:跨域限制只存在浏览器端!Postman、后端代码调用接口,不存在跨域。
Cookie:服务器发给浏览器,浏览器自动保存在本地的一小段文本信息 。 之后每次访问这个网站,浏览器会自动带上 Cookie 发给后端,不需要前端手动写代码。
举个生活化例子: 你登录淘宝,登录成功,淘宝后端给你的浏览器下发一个 Cookie,内容类似userId=1001。 以后你每次打开淘宝页面,浏览器自动把这个 Cookie 发给淘宝服务器。后端一看 Cookie,就知道:哦,是这个用户,不用重复登录。
特点:
- 存在浏览器本地(保存在电脑)
- 访问对应网站,自动随请求带上
- 有过期时间,可以后端设置
3.为什么webmvcconfigurer接口里面含有拦截器,拦截器不是属于servlet的规范吗?
❌ 误区纠正:拦截器(HandlerInterceptor)不属于 Servlet 规范! Servlet 规范只有 Filter 过滤器!!
- Filter:Servlet 原生,Tomcat 定义,和 Spring 无关。
- HandlerInterceptor 拦截器:SpringMVC 自己造的组件,不属于 Servlet,只在 SpringMVC 框架内部生效。
那WebMvcConfigurer#addInterceptors()是干嘛的? WebMvcConfigurer 是 SpringMVC 的扩展配置接口,用来注册、配置你写好的拦截器,交给 SpringMVC 管理 。 不是说拦截器写在这个接口里面,而是通过这个方法注册拦截器。
一句话总结: 拦截器是 SpringMVC 的,不是 Servlet;WebMvcConfigurer 提供方法,把拦截器注册进 SpringMVC。
4.在springmvc里面有一个配置是打开视图控制器的,要是不开视图控制器然后想要跳转HTML页面是不是要写很多controller?
视图控制器:addViewControllers(),就是你截图 WebMvcConfigurer 里的方法。 作用:不用写 Controller 方法,直接绑定 URL → 页面。
举例子:
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login.html");
}
访问/login直接跳 login 页面,不用写 Controller 类。
👉 如果不使用 addViewControllers: 想跳转页面,就必须写 Controller 方法:
@GetMapping("/login")
public String toLogin(){
return "login.html";
}
如果页面很多,就要写一堆这种空方法,非常重复。
5.异步支持是啥?
普通同步请求: 浏览器发请求 → Tomcat 线程卡住,一直占用这个线程,直到业务处理完返回结果。 Tomcat 线程池数量有限,如果慢请求很多,线程会被占满,新请求进不来,服务卡死。
异步请求: 请求进来,Tomcat 工作线程立刻释放,还给线程池。业务逻辑丢到另外一个自定义异步线程去跑。等业务处理完成,再返回给浏览器。
configureAsyncSupport就是 WebMvcConfigurer 里面,用来配置异步请求的线程池、超时时间。
适用场景:长耗时接口,比如导出大文件、远程调用很慢的第三方接口。
简单理解: 同步:服务员一直守着你这桌,等菜上完才能接待下一桌。 异步:服务员先离开去接待别的客人,后厨做好菜再通知服务员送过来。
静态资源映射方法
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
// 规则1:webjars静态资源映射
addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(),
"classpath:/META-INF/resources/webjars/");
// 规则2:通用静态资源映射
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(resource);
}
});
}
规则

静态资源
静态资源映射
静态资源缓存

3.欢迎页

4.Favicon
在静态资源目录下找favicon.ico
5.缓存实验

自定义静态资源规则

静态资源目录 和 静态资源缓存策略
访问直接写wj/什么 然后访问就行,不用写全称去找
容器中只要有一个WebMvcConfigurer就行
法1:

法2:SpringMVC 启动时,寻找容器中所有 WebMvcConfigurer 类型的 Bean,回调里面的配置方法,完成 MVC 自定义配置。


因为de那个类它是springmvc原生的,所以不能与SpringBoot融合,所以不能读yml,只能通过enable那个子类继承,重写遍历,然后利用 webmvcProperties 将配置给到mvc组件
- 你:
WebConfig implements WebMvcConfigurer,重写addResourceHandlers,写好添加资源映射的代码 - Spring 把 WebConfig 放进 IOC 容器
- De 通过 @Autowired 收集到 List<WebMvcConfigurer>(拿到你的 WebConfig)
- MVC 底层启动,调用 De 的 addResourceHandlers (registry)
- De for 循环:
c.addResourceHandlers(registry) - 执行你写的代码 → 往 registry 注册规则
- MVC 拿 registry 里面全部规则正式生效
3.路径匹配

模式转换
不能在路径中间加双**
spring.mvc.pathmatch.matching-strategy=path_pattern_parser
使用默认的路径的匹配功能就行由PathPatternParser
如果路径中间需要有双⭐,那就换成ant风格
内容协商
第三方发请求想要的数据不一样,假如接口多,不能只用请求头/person那种请求,所以说很麻烦,用一套系统如何适配多种,
多端内容适配
1.默认规则
请求头默认开启

2.效果演示

引入转换为xml格式的依赖 导包 ->@JacksonXmlRootElement标注解(在根标签例如:Person)
自定义内容返回
1.增加yaml返回支持
1. 增加 yaml 返回支持
导入依赖
xml
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
编写配置
properties
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
增加 HttpMessageConverter 组件,专门把对象写成yaml格式
2.思考:如何增加其他
3.内容协商原理 HttpMessageConverter
定制 HttpMessageConverter 来适配多端内容 协商
编写webMvcconfigurer提供的 configureMessageConverte来实现多端内容协商
1.@ResponseBody


2.webMvcconfigurer提供几种默认httpmessageconverters

那为什么ObjectMapper能写出来yaml
我给你拆得清清楚楚,一句话先点明:
ObjectMapper 本身不是只能写 JSON,它只是一个「序列化调度外壳」;真正决定输出是 JSON 还是 YAML 的,是传给它的 Factory。
1. 两个 Factory
JsonFactory:Jackson 默认的。负责生成 JSON 的字节
YAMLFactory(来自 jackson-dataformat-yaml):负责生成 YAML 的字节
java
运行
// JSON版本(默认)
ObjectMapper jsonMapper = new ObjectMapper(new JsonFactory());
// YAML版本
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
ObjectMapper 的代码不用改,writeValueAsString() 这个方法一模一样。
ObjectMapper 只负责:遍历你的 Java 对象、读取字段、调用 Factory 去输出字符。
Factory 才知道语法规则:什么时候加大括号{}、什么时候加冒号、换行(yaml)。
你可以这么理解:
ObjectMapper = 一个通用打印机
JsonFactory = JSON 墨盒,打出来是 JSON 格式
YAMLFactory = YAML 墨盒,打出来是 YAML 格式
打印机本身不变,换墨盒,输出格式就变了。
2. 重点:ObjectMapper 不懂 yaml 语法!
ObjectMapper 完全不知道 yaml 长啥样
是 YAMLFactory + YAMLGenerator 里面写好了 yaml 的格式规则:
用 : 分隔 key value
缩进表示层级
开头可选 ---
这些逻辑全在 YAMLFactory 里面。
ObjectMapper 只负责把「对象里面有哪些字段、值是多少」交给这个 Factory。
3. 和 HTTP、Content-Type 分开(承接刚才说的)
⚠️ 这个序列化,和 HTTP 没有半点关系!
你用 YAMLFactory 得到 yaml 字符串,只是一段文本:
yaml
name: 小明
age: 18
ObjectMapper + YAMLFactory 只负责生产这段文字。
至于 HTTP 响应头写不写 Content-Type: application/yaml,是 HttpMessageConverter 的事情,和它俩无关。
4. 延伸:还能换别的 Factory
jackson 还有别的扩展:
CBORFactory:二进制格式 CBOR
SmileFactory:另一种二进制
它们全都可以丢给 ObjectMapper,一套 API 通用。
这就是 Jackson 的设计:API 统一,格式由底层 Factory 插拔切换
极简一句话总结
ObjectMapper 是通用框架,不绑定 JSON;
传入不同的 Factory,就切换成不同格式;
YAMLFactory 里面内置 yaml 语法规则,所以它就能输出 yaml。
要不要我给你对比一下,同一个 User 对象,JsonFactory 和 YAMLFactory 输出结果?
代码里ObjectMapper怎么跟工厂联合的
先看源码构造方法
java
运行
// ObjectMapper 有一个构造,接收 JsonFactory(父类!YAMLFactory 是它的子类)
public ObjectMapper(JsonFactory factory) {
this(factory, null, null);
}
类继承关系:
YAMLFactory extends JsonFactory
所以可以直接传给 ObjectMapper。
1. 联合的代码(最简单写法)
java
运行
// 1. 先造工厂(YAML工厂)
JsonFactory factory = new YAMLFactory();
// 2. 把工厂传给ObjectMapper,绑定在一起
ObjectMapper mapper = new ObjectMapper(factory);
User user = new User();
user.setName("张三");
// 3. 调用writeValueAsString
String yaml = mapper.writeValueAsString(user);
System.out.println(yaml);
2. 内部到底怎么联动(核心流程)
当你调用 mapper.writeValueAsString(user) 的时候,内部步骤:
ObjectMapper 拿到你的 Java 对象,开始反射,拿到类、字段、值
ObjectMapper 去找自己持有的那个 JsonFactory(就是你传进来的 YAMLFactory)
调用 factory.createGenerator() 创建一个 Generator(生成器)
JsonFactory → JsonGenerator(生成 json 符号:{ } , :)
YAMLFactory → YAMLGenerator(生成 yaml 符号:换行、缩进、:)
ObjectMapper 把字段名、字段值,一句一句丢给 Generator
比如告诉生成器:key 是 name,value 是张三
Generator 按照自己格式的语法,拼出文本
最后返回拼好的字符串
✅ 关键点:
ObjectMapper 只负责【解析对象、遍历属性】
Generator 负责【按格式输出字符】
Factory 负责【生产对应的 Generator】
画图理解调用链:
plaintext
mapper.writeValueAsString(对象)
↓
ObjectMapper(反射拿字段)
↓
this._factory.createGenerator()
↓
YAMLFactory 造出 YAMLGenerator
↓
ObjectMapper 循环调用 generator.writeFieldName / writeString
↓
YAMLGenerator 输出 yaml缩进、冒号、换行
3. 对比默认 JSON 版本
java
运行
// 默认无参构造,底层自动 new JsonFactory()
ObjectMapper jsonMapper = new ObjectMapper();
// 等价于
ObjectMapper jsonMapper = new ObjectMapper(new JsonFactory());
4. 你还可以提前给工厂配置,再传给 Mapper
java
运行
// 先配置工厂
YAMLFactory yamlFactory = new YAMLFactory()
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
// 再绑定
ObjectMapper mapper = new ObjectMapper(yamlFactory);
工厂的配置,会被它产出的 Generator 继承。
5. 一句话总结联合关系
ObjectMapper 持有一个 JsonFactory 的成员变量;创建 mapper 的时候把工厂传进去,两者就绑定了。序列化的时候 mapper 调用这个工厂来产出对应的 Generator,由 Generator 输出对应格式文本。
补充区分(别混)
Factory:生产 Generator
Generator:真正输出符号(json /yaml 语法)
ObjectMapper:调度,反射读取 Java 对象的数据,喂给 Generator
要不要我把这一套,套进之前说的 HttpMessageConverter,写一个返回 YAML 的消息转换器完整代码?
YAMLFactory怎么传的
1、最简单直接传(核心写法)
YAMLFactory 是 JsonFactory 的子类,而 ObjectMapper 的构造方法接收 JsonFactory,所以直接把 new YAMLFactory() 塞进去就行:
java
运行
// 第一步:创建YAML工厂
YAMLFactory yamlFactory = new YAMLFactory();
// 第二步:传给ObjectMapper的构造方法,绑定
ObjectMapper mapper = new ObjectMapper(yamlFactory);
等价简写一行:
java
运行
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
底层源码:
java
运行
public ObjectMapper(JsonFactory factory) {
this(factory, null, null);
}
因为 YAMLFactory extends JsonFactory,符合参数类型,编译通过。
2、带配置再传递(常用,关掉 yaml 开头的 ---)
先配置工厂,再传给 mapper:
java
运行
// 先new工厂,设置特性
YAMLFactory yamlFactory = new YAMLFactory()
// 关闭yaml默认的 --- 文档标记
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
// 把配置好的工厂传进去
ObjectMapper mapper = new ObjectMapper(yamlFactory);
3、❌ 错误写法(不能后面 set,这个坑很多人踩)
ObjectMapper 没有 setFactory 方法!
工厂只能在构造的时候传,一旦 mapper 创建完成,工厂就固定了,不能中途替换。
java
运行
// 不行!没有这个方法
mapper.setFactory(new YAMLFactory());
👉 想要换格式,只能重新 new 一个 ObjectMapper,传入新工厂。
4、传进去之后,内部怎么存的
ObjectMapper 内部有一个成员变量:
java
运行
protected final JsonFactory _jsonFactory;
你传进去的 YAMLFactory,就赋值给这个 _jsonFactory。
后面调用 writeValueAsString 的时候,mapper 就拿这个 _jsonFactory 去创建 YAMLGenerator。
5、对比默认 JSON 的情况
java
运行
// 无参构造,底层自动 new JsonFactory()
ObjectMapper jsonMapper = new ObjectMapper();
// 等价
ObjectMapper jsonMapper = new ObjectMapper(new JsonFactory());
一句话总结
YAMLFactory 在 new ObjectMapper 的时候,作为构造参数传进去;工厂一旦传入绑定,就不能修改。
.3.http写法
webconfig重写

配置文件


4.WebMvcConfiguartionSupport
提供了很多默认设置
判断系统中是否有相应的类,如果有就加入相应的HttpMessageConverter
