web场景,默认配置:
- 包含了 ContentNegotiatingViewResolver 和 BeanNameViewResolver 组件,方便视图解析
- 默认的静态资源处理机制: 静态资源放在 static 文件夹下即可直接访问
- 自动注册了 Converter,GenericConverter,Formatter组件,适配常见数据类型转换和格式化需求
- 支持 HttpMessageConverters,可以方便返回json等数据类型
- 注册 MessageCodesResolver,方便国际化及错误消息处理
- 支持 静态 index.html
- 自动使用ConfigurableWebBindingInitializer,实现消息处理、数据绑定、类型转化、数据校验等功能
- 如果想保持 boot mvc 的默认配置,并且自定义更多的 mvc 配置,如:interceptors, formatters, view controllers 等。可以使用@Configuration注解添加一个 WebMvcConfigurer 类型的配置类,并不要标注 @EnableWebMvc
- 如果想保持 boot mvc 的默认配置,但要自定义核心组件实例,比如:RequestMappingHandlerMapping, RequestMappingHandlerAdapter, 或ExceptionHandlerExceptionResolver,给容器中放一个 WebMvcRegistrations 组件即可
- 如果想全面接管 Spring MVC,@Configuration 标注一个配置类,并加上 @EnableWebMvc注解,实现 WebMvcConfigurer 接口
web开发有三种方式:
方式 | 用法 | 效果 | |
全自动 | 直接编写控制器逻辑 | 全部使用自动配置默认效果 | |
手自一体 | @Configuration + 配置WebMvcConfigurer +配置 WebMvcRegistrations | 不要标注@ EnableWebMvc | 保留自动配置效果 手动设置部分功能 定义MVC底层组件 |
全手动 | @Configuration + 配置WebMvcConfigurer | 标注@ EnableWebMvc | 禁用自动配置效果 全手动设置 |
如果有定制化需求,给容器中写一个配置类,在配置类上加上@Configuration注解,并不要标注@EnableWebMvc,让这个配置类实现WebMvcConfigurer接口,就可以实现手自一体的效果
- WebMvcAutoConfiguration会给项目配置两个Filter:
HiddenHttpMethodFilter
:用于页面表单提交Rest请求(GET、POST、PUT、DELETE)FormContentFilter
: 表单内容Filter,GET(数据放URL后面)、POST(数据放请求体)请求可以携带数据,PUT、DELETE 的数据会被忽略。配置了FormContentFilter
后PUT、DELETE的数据就不会被忽略- WebMvcAutoConfiguration给容器中放了
WebMvcConfigurer
组件,给SpringMVC添加各种定制功能(用手自一体方式开发时会派上用场)
- WebMvcConfigurer接口提供了SpringMVC底层的所有组件入口
- 所有的功能最终会和配置文件进行绑定
- WebMvcProperties:和
spring.mvc
配置绑定- WebProperties:和
spring.web
配置绑定
- 规则一:访问:
/webjars/**
路径就去classpath:/META-INF/resources/webjars/
下找资源.- 规则二:访问:
/**
路径就去静态资源默认的四个位置找资源
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
- 规则三:静态资源默认都有缓存规则的设置
- 如果浏览器访问了一个静态资源,会将其缓存,如果服务器中这个资源没有发生变化,下次访问时,可以直接使用其浏览器中的该静态资源的缓存,而不用给服务器发请求。
- 有几个关于缓存的配置(直接通过配置文件spring.web
)
- cachePeriod:缓存周期,即多久不用找服务器再请求该静态资源。默认没有缓存周期,以秒为单位。
- cacheControl:HTTP缓存,即会存储与请求相关联的响应,并将存储的响应复用于后续请求
- useLastModified:是否使用最后一次修改的资源,配合HTTP缓存使用,默认为true。即以最后一次修改资源的时间和使用其缓存资源进行对比,来判断是否要再次请求。
欢迎页规则在 WebMvcAutoConfiguration 中进行了定义:
- 在静态资源目录下找 index.html
- 没有就在 templates下找index模板页
Favicon是代表浏览器标签页的图标的,浏览器会到服务器静态资源目录下找 favicon.ico,再以其资源作为标签页的图标
在配置文件中自定义静态资源规则:
- #1、spring.web:
- # 1.配置国际化的区域信息
- # 2.静态资源策略(开启、处理链、缓存)
-
- #开启静态资源映射规则
- spring.web.resources.add-mappings=true
-
- #设置缓存
- spring.web.resources.cache.period=3600
- ##缓存详细合并项控制,覆盖period配置:
- ## 浏览器第一次请求服务器,服务器告诉浏览器此资源缓存7200秒,7200秒以内的所有此资源访问不用发给服务器请求,7200秒以后发请求给服务器
- spring.web.resources.cache.cachecontrol.max-age=7200
- ## 共享缓存
- spring.web.resources.cache.cachecontrol.cache-public=true
- #使用资源 last-modified 时间,来对比服务器和浏览器的资源是否相同没有变化。相同返回 304
- spring.web.resources.cache.use-last-modified=true
-
- #自定义静态资源文件夹位置
- spring.web.resources.static-locations=classpath:/a/,classpath:/b/,classpath:/static/
-
- #2、 spring.mvc
- ## 2.1. 自定义webjars路径前缀
- spring.mvc.webjars-path-pattern=/wj/**
- ## 2.2. 静态资源访问路径前缀
- spring.mvc.static-path-pattern=/static/**
在配置类中自定义静态资源规则:(第一种写法)
- @Configuration//这是一个配置类
- public class MyConfig implements WebMvcConfigurer {
-
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- //保留以前规则
- //自己写新的规则。
- registry.addResourceHandler("/static/**")
- .addResourceLocations("classpath:/a/","classpath:/b/")
- .setCacheControl(CacheControl.maxAge(1180, TimeUnit.SECONDS));
- }
- }
(第二种写法)
- @Configuration //这是一个配置类,给容器中放一个 WebMvcConfigurer 组件,就能自定义底层
- public class MyConfig /*implements WebMvcConfigurer*/ {
-
- @Bean
- public WebMvcConfigurer webMvcConfigurer(){
- return new WebMvcConfigurer() {
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- registry.addResourceHandler("/static/**")
- .addResourceLocations("classpath:/a/", "classpath:/b/")
- .setCacheControl(CacheControl.maxAge(1180, TimeUnit.SECONDS));
- }
- };
- }
-
- }
容器中只要有一个 WebMvcConfigurer 组件。配置的底层行为都会生效
- WebMvcAutoConfiguration 是一个自动配置类,它里面有一个
EnableWebMvcConfiguration
EnableWebMvcConfiguration
继承于DelegatingWebMvcConfiguration
,这两个都生效DelegatingWebMvcConfiguration
利用 DI 把容器中 所有WebMvcConfigurer
注入进来- 当调用
DelegatingWebMvcConfiguration
的方法配置底层规则,而DelegatingWebMvcConfiguration
调用所有WebMvcConfigurer
的配置底层方法。
Ant 风格的路径模式语法具有以下规则:
- *:表示任意数量的字符。
- ?:表示任意一个字符。
- **:表示任意数量的目录。
- {}:表示一个命名的模式占位符。
- []:表示字符集合,例如[a-z]表示小写字母,[a-z]+表示任意个小写字符
- 要为{}中的路径变量指定字符集合,可以使用{路径变量:[字符集合]}
PathPatternParser比起Ant,效率更高,兼容 AntPathMatcher语法,并支持更多类型的路径模式,即使用PathPatternParser路径匹配时还能继续使用Ant的语法规则,但是有一点有改变:
- PathPatternParser用**表示任意层目录时,只能将**放在最末尾
- 如果要将**放在路径中间而不是末尾,需要将路径匹配改回Ant,改回Ant配置方式如下:
# 改变路径匹配策略: # ant_path_matcher 老版策略; # path_pattern_parser 新版策略; spring.mvc.pathmatch.matching-strategy=ant_path_matcher
- SpringBoot3默认是PathPatternParser的匹配规则
内容协商能让一套系统适配多端数据返回
- SpringBoot 多端内容适配。
- 基于请求头内容协商:(默认开启)
- 客户端向服务端发送请求,携带HTTP标准的Accept请求头。
- Accept:
application/json
、text/xml
、text/yaml
- 服务端根据客户端请求头期望的数据类型进行动态返回
- 基于请求参数内容协商:(需要开启)
- 发送请求 GET /projects/spring-boot?format=json
- 匹配到 @GetMapping("/projects/spring-boot")
- 根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置】
- 发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据
如果要返回xml数据格式:
- <dependency>
- <groupId>com.fasterxml.jackson.dataformatgroupId>
- <artifactId>jackson-dataformat-xmlartifactId>
- dependency>
- @JacksonXmlRootElement // 可以写出为xml文档
- @Data
- public class Person {
- private Long id;
- private String userName;
- private String email;
- private Integer age;
- }
开启基于请求参数的内容协商:
- # 开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启
- spring.mvc.contentnegotiation.favor-parameter=true
- # 指定内容协商时使用的参数名。默认是 format
- spring.mvc.contentnegotiation.parameter-name=type
可以通过定制HttpMessageConverter类来实现多端内容协商,通过编写WebMvcConfigurer提供的configureMessageConverters底层,来修改底层的MessageConverter,以实现定制多端内容协商。
- @ResponseBody注解由HttpMessageConverter处理
- 请求来到DispatcherServlet的doDispatch方法进行处理
- 在HandlerMapping中校验成功后,来到HandlerAdapter,利用HandlerAdapter调用handler方法(使用反射的方法invokeHandlerMethod()来执行目标方法)
- 目标方法执行之前,准备好了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler
- HandlerMethodArgumentResolver:参数解析器,确定目标方法的每个参数值
- HandlerMethodReturnValueHandler:返回值处理器,确定目标方法的返回值怎么处理
- invokeAndHandler方法真正执行目标方法,目标方法执行完成,会返回返回值对象
- 找到返回值处理器HandlerMethodReturnValueHandler
- 最终找到RequestResponseBodyMethodProcessor,能处理标注了@RequestBody注解的方法
- RequestResponseBodyMethodProcessor调用writeWithMessageConverters,利用MessageConverter将返回值写出去
- HttpMessageConverter会先进行内容协商
- 遍历所有的MessageConverter,找到目标内容类型的数据
- 要返回json形式的数据的话,MappingJackson2HttpMessageConverter支持写出json数据,最后写出数据
WebMvcAutoConfiguration提供几种默认HttpMessageConverters
EnableWebMvcConfiguration
通过addDefaultHttpMessageConverters
添加了默认的MessageConverter
;如下:
ByteArrayHttpMessageConverter
: 支持字节数据读写StringHttpMessageConverter
: 支持字符串读写ResourceHttpMessageConverter
:支持资源读写ResourceRegionHttpMessageConverter
: 支持分区资源写出AllEncompassingFormHttpMessageConverter
:支持表单xml/json读写MappingJackson2HttpMessageConverter
: 支持请求响应体Json读写
系统提供默认的MessageConverter 功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的HttpMessageConverter
以导入yaml格式为例:
- <dependency>
- <groupId>com.fasterxml.jackson.dataformatgroupId>
- <artifactId>jackson-dataformat-yamlartifactId>
- dependency>
- Person person = new Person();
- person.setId(1L);
- person.setUserName("张三");
- person.setEmail("aaa@qq.com");
- person.setAge(18);
-
- //取消文档的开始标记(disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER))
- YAMLFactory factory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
- ObjectMapper mapper = new ObjectMapper(factory);
-
- String s = mapper.writeValueAsString(person);
编写配置
- #新增一种媒体类型
- spring.mvc.contentnegotiation.media-types.yaml=text/yaml
自定义Converter
- public class MyYamlHttpMessageConverter extends AbstractHttpMessageConverter
- private ObjectMapper objectMapper = null;
- public MyYamlHttpMessageConverter(){
- //告诉SpringBoot这个MessageConverter支持哪种媒体类型
- super(new MediaType("text","yaml", Charset.forName("UTF-8")));
- YAMLFactory factory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
- this.objectMapper = new ObjectMapper(factory);
-
- }
- @Override
- protected boolean supports(Class> clazz) {//是否支持指定类型的转化
- //可以在方法内进行类型判断,返回相应的true和false
- return true;
- }
-
- @Override//指定请求中的数据如何转化
- protected Object readInternal(Class> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
- return null;
- }
-
- @Override//指定响应中的数据如何转化
- protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
- try (OutputStream body = outputMessage.getBody()){
- this.objectMapper.writeValue(body,o);
- }
- }
- }
最后在WebMvcConfigurer配置类中配置其Converter即可
- @Bean
- public WebMvcConfigurer webMvcConfigurer(){
- return new WebMvcConfigurer() {
- @Override //配置一个能把对象转为yaml的messageConverter
- public void configureMessageConverters(List
> converters{ - converters.add(new MyYamlHttpMessageConverter());
- }
- };
- }
-
配置步骤:
- 配置媒体类型支持:
- 编写对应的
HttpMessageConverter
,要告诉Boot这个支持的媒体类型- 把MessageConverter组件加入到底层
- 容器中放一个
WebMvcConfigurer
组件,并配置底层的MessageConverter