SpringBoot3核心特性-Web开发

news2024/9/20 8:01:08

目录

  • 传送门
  • 前言
  • 一、WebMvcAutoConfiguration原理
    • 1、生效条件
    • 2、效果
    • 3、WebMvcConfigurer接口
    • 4、静态资源规则源码
    • 5、EnableWebMvcConfiguration 源码
    • 6、为什么容器中放一个WebMvcConfigurer就能配置底层行为
    • 7、WebMvcConfigurationSupport
  • 二、Web场景
    • 1、自动配置
    • 2、默认效果
  • 三、静态资源
    • 1、默认规则
    • 2、自定义静态资源规则
  • 四、路径匹配
    • 1、Ant风格路径用法
    • 2、模式切换
  • 五、内容协商
    • 1、多端内容适配
      • 1.1、默认规则
      • 1.2、 效果演示
      • 1.3、 配置协商规则与支持类型
    • 2、自定义内容返回
      • 2.1、增加yaml返回支持
      • 2.2、思考:如何增加其他
      • 2.3、HttpMessageConverter的示例写法
    • 3、内容协商原理HttpMessageConverter
      • 3.1、@ResponseBody由HttpMessageConverter处理
      • 3.2、WebMvcAutoConfiguration提供几种默认HttpMessageConverters
  • 六、模板引擎
    • 1、Thymeleaf整合
    • 2、基础语法
      • 2.1、核心用法
      • 2.2、语法示例
    • 3、属性设置
    • 4、遍历
    • 5、判断
    • 6、属性优先级
    • 7、行内写法
    • 8、变量选择
    • 9、模板布局
    • 10、devtools
  • 七、国际化
  • 八、错误处理
    • 1、默认机制
    • 2、自定义错误响应
    • 3、最佳实战
  • 九、嵌入式容器
    • 1、自动配置原理
    • 2、自定义
    • 3、最佳实战
  • 十、全面接管SpringMVC
    • 1、WebMvcAutoConfiguration 到底自动配置了哪些规则
    • 2、@EnableWebMvc 禁用默认行为
    • 3、WebMvcConfigurer 功能
  • 十一、最佳实战
    • 1、三种方式
    • 2、两种模式
  • 十二、Web新特性
    • 1、Problemdetails
    • 2、函数式Web
      • 2.1、场景
      • 2.2、核心类
      • 2.3、示例

传送门

SpringMVC的源码解析(精品)
Spring6的源码解析(精品)
SpringBoot3框架(精品)
MyBatis框架(精品)
MyBatis-Plus
SpringDataJPA
SpringCloudNetflix
SpringCloudAlibaba(精品)
Shiro
SpringSecurity
java的LOG日志框架
Activiti(敬请期待)
JDK8新特性
JDK9新特性
JDK10新特性
JDK11新特性
JDK12新特性
JDK13新特性
JDK14新特性
JDK15新特性
JDK16新特性
JDK17新特性
JDK18新特性
JDK19新特性
JDK20新特性
JDK21新特性
其他技术文章传送门入口

前言

由于面试问到的比较多,而且做java开发这块还是需要真正掌握的。
现有笔记尚硅谷雷锋阳老师的:SpringBoot3全栈指南,是我目前见过的最好笔记了。
参考视频尚硅谷雷锋阳老师的:SpringBoot零基础教程,面试&加薪必会,视频是24小时31分钟的高质量教程。
参考代码:https://gitee.com/leifengyang/spring-boot-3

最经典的20个Spring Boot面试题,95%以上会被问到,不服来战

为了防止雷锋阳老师的日志查看不到,这里分类整理一下。下面文章不定时更新

SpringBoot3核心特性-快速入门
SpringBoot3核心特性-Web开发
SpringBoot3核心特性-数据访问
SpringBoot3核心特性-基础特性
SpringBoot3核心特性-核心原理
SpringBoot3场景整合
SpringBoot3响应式编程全套-Reactor核心
SpringBoot3响应式编程全套-Spring Webflux
SpringBoot3响应式编程全套-R2DBC
SpringBoot3响应式编程全套-Spring Security Reactive

一、WebMvcAutoConfiguration原理

SpringBoot的Web开发能力,由SpringMVC提供。

1、生效条件

@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class }) //在这些自动配置之后
@ConditionalOnWebApplication(type = Type.SERVLET) //如果是web应用就生效,类型SERVLET、REACTIVE 响应式web
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //容器中没有这个Bean,才生效。默认就是没有
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)//优先级
@ImportRuntimeHints(WebResourcesRuntimeHints.class)
public class WebMvcAutoConfiguration { 
}

2、效果

  1. 放了两个Filter:
    a. HiddenHttpMethodFilter;页面表单提交Rest请求(GET、POST、PUT、DELETE)
    b. FormContentFilter: 表单内容Filter,GET(数据放URL后面)、POST(数据放请求体)请求可以携带数据,PUT、DELETE 的请求体数据会被忽略
  2. 给容器中放了WebMvcConfigurer组件;给SpringMVC添加各种定制功能
    a. 所有的功能最终会和配置文件进行绑定
    b. WebMvcProperties: spring.mvc配置文件
    c. WebProperties: spring.web配置文件
	@Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class) //额外导入了其他配置
	@EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware{
        
    }

3、WebMvcConfigurer接口

提供了配置SpringMVC底层的所有组件入口
在这里插入图片描述

4、静态资源规则源码

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    //1、
    addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(),
            "classpath:/META-INF/resources/webjars/");
    addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
        registration.addResourceLocations(this.resourceProperties.getStaticLocations());
        if (this.servletContext != null) {
            ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
            registration.addResourceLocations(resource);
        }
    });
}
  1. 规则一:访问: /webjars/**路径就去 classpath:/META-INF/resources/webjars/下找资源.
    a. maven 导入依赖
    b. 这些依赖通常包含前端库和框架,通过访问/webjars/路径来获取这些静态资源
  2. 规则二:访问: /**路径就去 静态资源默认的四个位置找资源
    a. classpath:/META-INF/resources/
    b. classpath:/resources/
    c. classpath:/static/
    d. classpath:/public/
  3. 规则三:静态资源默认都有缓存规则的设置
    a. 所有缓存的设置,直接通过配置文件: spring.web
    b. cachePeriod: 缓存周期; 多久不用找服务器要新的。 默认没有,以s为单位
    c. cacheControl: HTTP缓存控制;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching
    d. useLastModified:是否使用最后一次修改。配合HTTP Cache规则
    如果浏览器访问了一个静态资源 index.js,如果服务这个资源没有发生变化,下次访问的时候就可以直接让浏览器用自己缓存中的东西,而不用给服务器发请求。
registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());

5、EnableWebMvcConfiguration 源码

//SpringBoot 给容器中放 WebMvcConfigurationSupport 组件。
//我们如果自己放了 WebMvcConfigurationSupport 组件,Boot的WebMvcAutoConfiguration都会失效。
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware 
{

    
}

6、为什么容器中放一个WebMvcConfigurer就能配置底层行为

  1. WebMvcAutoConfiguration 是一个自动配置类,它里面有一个 EnableWebMvcConfiguration
  2. EnableWebMvcConfiguration继承与 DelegatingWebMvcConfiguration,这两个都生效
  3. DelegatingWebMvcConfiguration利用 DI 把容器中 所有 WebMvcConfigurer 注入进来
  4. 别人调用 DelegatingWebMvcConfiguration 的方法配置底层规则,而它调用所有 WebMvcConfigurer的配置底层方法。

7、WebMvcConfigurationSupport

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

jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
				ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);

二、Web场景

1、自动配置

1、整合web场景

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

2、引入了 autoconfigure功能
3、@EnableAutoConfiguration注解使用@Import(AutoConfigurationImportSelector.class)批量导入组件
4、加载 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中配置的所有组件
5、所有自动配置类如下

org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
====以下是响应式web场景和现在的没关系======
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveMultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebSessionIdResolverAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
================以上没关系=================
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

6、绑定了配置文件的一堆配置项
● 1、SpringMVC的所有配置 spring.mvc
● 2、Web场景通用配置 spring.web
● 3、文件上传配置 spring.servlet.multipart
● 4、服务器的配置 server: 比如:编码方式

2、默认效果

默认配置:

  1. 包含了 ContentNegotiatingViewResolver 和 BeanNameViewResolver 组件,方便视图解析
  2. 默认的静态资源处理机制: 静态资源放在 static 文件夹下即可直接访问
  3. 自动注册了 Converter,GenericConverter,Formatter组件,适配常见数据类型转换和格式化需求
  4. 支持 HttpMessageConverters,可以方便返回json等数据类型
  5. 注册 MessageCodesResolver,方便国际化及错误消息处理
  6. 支持 静态 index.html
  7. 自动使用ConfigurableWebBindingInitializer,实现消息处理、数据绑定、类型转化、数据校验等功能
    重要:
    ● 如果想保持 boot mvc 的默认配置,并且自定义更多的 mvc 配置,如:interceptors, formatters, view controllers 等。可以使用@Configuration注解添加一个 WebMvcConfigurer 类型的配置类,并不要标注 @EnableWebMvc
    ● 如果想保持 boot mvc 的默认配置,但要自定义核心组件实例,比如:RequestMappingHandlerMapping, RequestMappingHandlerAdapter, 或ExceptionHandlerExceptionResolver,给容器中放一个 WebMvcRegistrations 组件即可
    ● 如果想全面接管 Spring MVC,@Configuration 标注一个配置类,并加上 @EnableWebMvc注解,实现 WebMvcConfigurer 接口

三、静态资源

1、默认规则

  1. 静态资源映射
    静态资源映射规则在 WebMvcAutoConfiguration 中进行了定义:

  2. /webjars/** 的所有路径 资源都在 classpath:/META-INF/resources/webjars/

  3. /** 的所有路径 资源都在 classpath:/META-INF/resources/、classpath:/resources/、classpath:/static/、classpath:/public/

  4. 所有静态资源都定义了缓存规则。【浏览器访问过一次,就会缓存一段时间】,但此功能参数无默认值
    a. period: 缓存间隔。 默认 0S;
    b. cacheControl:缓存控制。 默认无;
    c. useLastModified:是否使用lastModified头。 默认 false;

  5. 静态资源缓存
    如前面所述

  6. 所有静态资源都定义了缓存规则。【浏览器访问过一次,就会缓存一段时间】,但此功能参数无默认值
    a. period: 缓存间隔。 默认 0S;
    b. cacheControl:缓存控制。 默认无;
    c. useLastModified:是否使用lastModified头。 默认 false;

  7. 欢迎页
    欢迎页规则在 WebMvcAutoConfiguration 中进行了定义:

  8. 在静态资源目录下找 index.html

  9. 没有就在 templates下找index模板页

  10. Favicon

  11. 在静态资源目录下找 favicon.ico

  12. 缓存实验

server.port=9000

#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
#使用资源 last-modified 时间,来对比服务器和浏览器的资源是否相同没有变化。相同返回 304
spring.web.resources.cache.use-last-modified=true

2、自定义静态资源规则

自定义静态资源路径、自定义缓存规则

  1. 配置方式
    spring.mvc: 静态资源访问前缀路径
    spring.web:
    ● 静态资源目录
    ● 静态资源缓存策略
#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/**
  1. 代码方式
    ● 容器中只要有一个 WebMvcConfigurer 组件。配置的底层行为都会生效
    ● @EnableWebMvc //禁用boot的默认配置
@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));
            }
        };
    }

}

四、路径匹配

Spring5.3 之后加入了更多的请求路径匹配的实现策略;
以前只支持 AntPathMatcher 策略, 现在提供了 PathPatternParser 策略。并且可以让我们指定到底使用那种策略。

1、Ant风格路径用法

Ant 风格的路径模式语法具有以下规则:
● 单*:表示任意数量的字符。
● ?:表示任意一个字符。
● 双*:表示任意数量的目录。
● {}:表示一个命名的模式占位符。
● []:表示字符集合,例如[a-z]表示小写字母。
例如:
.html 匹配任意名称,扩展名为.html的文件。
● /folder1/
/.java 匹配在folder1目录下的任意两级目录下的.java文件。
● /folder2/**/
.jsp 匹配在folder2目录下任意目录深度的.jsp文件。
● /{type}/{id}.html 匹配任意文件名为{id}.html,在任意命名的{type}目录下的文件。
注意:Ant 风格的路径模式语法中的特殊字符需要转义,如:
● 要匹配文件路径中的星号,则需要转义为\*。
● 要匹配文件路径中的问号,则需要转义为\?。

2、模式切换

AntPathMatcher 与 PathPatternParser
● PathPatternParser 在 jmh 基准测试下,有 6~8 倍吞吐量提升,降低 30%~40%空间分配率
● PathPatternParser 兼容 AntPathMatcher语法,并支持更多类型的路径模式
● PathPatternParser “**” 多段匹配的支持仅允许在模式末尾使用

    @GetMapping("/a*/b?/{p1:[a-f]+}")
    public String hello(HttpServletRequest request, 
                        @PathVariable("p1") String path) {

        log.info("路径变量p1: {}", path);
        //获取请求路径
        String uri = request.getRequestURI();
        return uri;
    }

总结:
● 使用默认的路径匹配规则,是由 PathPatternParser 提供的
● 如果路径中间需要有 **,替换成ant风格路径

# 改变路径匹配策略:
# ant_path_matcher 老版策略;
# path_pattern_parser 新版策略;
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

铁哥备注:springboot2.6以后,默认的匹配策略已经改为PathPatternParser。

五、内容协商

一套系统适配多端数据返回
在这里插入图片描述

1、多端内容适配

1.1、默认规则

  1. SpringBoot 多端内容适配。
    1.1. 基于请求头内容协商:(默认开启)
    1.1.1. 客户端向服务端发送请求,携带HTTP标准的Accept请求头。
    1.1.1.1. Accept: application/json、text/xml、text/yaml
    1.1.1.2. 服务端根据客户端请求头期望的数据类型进行动态返回
    1.2. 基于请求参数内容协商:(需要开启)
    1.2.1. 发送请求 GET /projects/spring-boot?format=json
    1.2.2. 匹配到 @GetMapping(“/projects/spring-boot”)
    1.2.3. 根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置】
    1.2.4. 发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据

1.2、 效果演示

请求同一个接口,可以返回json和xml不同格式数据

1.引入支持写出xml内容依赖

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2.标注注解

@JacksonXmlRootElement  // 可以写出为xml文档
@Data
public class Person {
    private Long id;
    private String userName;
    private String email;
    private Integer age;
}

3.开启基于请求参数的内容协商

# 开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启
spring.mvc.contentnegotiation.favor-parameter=true
# 指定内容协商时使用的参数名。默认是 format
spring.mvc.contentnegotiation.parameter-name=type

4.效果
在这里插入图片描述在这里插入图片描述

1.3、 配置协商规则与支持类型

1.修改内容协商方式

#使用参数进行内容协商
spring.mvc.contentnegotiation.favor-parameter=true  
#自定义参数名,默认为format
spring.mvc.contentnegotiation.parameter-name=myparam 

2.大多数 MediaType 都是开箱即用的。也可以自定义内容类型,如:

spring.mvc.contentnegotiation.media-types.yaml=text/yaml

2、自定义内容返回

2.1、增加yaml返回支持

导入依赖

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

把对象写出成YAML

    public static void main(String[] args) throws JsonProcessingException {
        Person person = new Person();
        person.setId(1L);
        person.setUserName("张三");
        person.setEmail("aaa@qq.com");
        person.setAge(18);

        YAMLFactory factory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
        ObjectMapper mapper = new ObjectMapper(factory);

        String s = mapper.writeValueAsString(person);
        System.out.println(s);
    }

编写配置

#新增一种媒体类型
spring.mvc.contentnegotiation.media-types.yaml=text/yaml

增加HttpMessageConverter组件,专门负责把对象写出为yaml格式

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override //配置一个能把对象转为yaml的messageConverter
            public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new MyYamlHttpMessageConverter());
            }
        };
    }

2.2、思考:如何增加其他

● 配置媒体类型支持:
○ spring.mvc.contentnegotiation.media-types.yaml=text/yaml
● 编写对应的HttpMessageConverter,要告诉Boot这个支持的媒体类型
○ 按照3的示例
● 把MessageConverter组件加入到底层
○ 容器中放一个WebMvcConfigurer 组件,并配置底层的MessageConverter

2.3、HttpMessageConverter的示例写法

public class MyYamlHttpMessageConverter extends AbstractHttpMessageConverter<Object> {

    private ObjectMapper objectMapper = null; //把对象转成yaml

    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) {
        //只要是对象类型,不是基本类型
        return true;
    }

    @Override  //@RequestBody
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override //@ResponseBody 把对象怎么写出去
    protected void writeInternal(Object methodReturnValue, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

        //try-with写法,自动关流
        try(OutputStream os = outputMessage.getBody()){
            this.objectMapper.writeValue(os,methodReturnValue);
        }

    }
}

3、内容协商原理HttpMessageConverter

● HttpMessageConverter 怎么工作?合适工作?
● 定制 HttpMessageConverter 来实现多端内容协商
● 编写WebMvcConfigurer提供的configureMessageConverters底层,修改底层的MessageConverter

3.1、@ResponseBody由HttpMessageConverter处理

标注了@ResponseBody的返回值 将会由支持它的 HttpMessageConverter写给浏览器

  1. 如果controller方法的返回值标注了 @ResponseBody 注解
    1.1. 请求进来先来到DispatcherServlet的doDispatch()进行处理
    1.2. 找到一个 HandlerAdapter 适配器。利用适配器执行目标方法
    1.3. RequestMappingHandlerAdapter来执行,调用invokeHandlerMethod()来执行目标方法
    1.4. 目标方法执行之前,准备好两个东西
    1.4.1. HandlerMethodArgumentResolver:参数解析器,确定目标方法每个参数值
    1.4.2. HandlerMethodReturnValueHandler:返回值处理器,确定目标方法的返回值改怎么处理
    1.5. RequestMappingHandlerAdapter 里面的invokeAndHandle()真正执行目标方法
    1.6. 目标方法执行完成,会返回返回值对象
    1.7. 找到一个合适的返回值处理器 HandlerMethodReturnValueHandler
    1.8. 最终找到 RequestResponseBodyMethodProcessor能处理 标注了 @ResponseBody注解的方法
    1.9. RequestResponseBodyMethodProcessor 调用writeWithMessageConverters ,利用MessageConverter把返回值写出去

上面解释:@ResponseBody由HttpMessageConverter处理

2.HttpMessageConverter 会先进行内容协商
2.1. 遍历所有的MessageConverter看谁支持这种内容类型的数据
2.2. 默认MessageConverter有以下
在这里插入图片描述
2.3 最终因为要json所以MappingJackson2HttpMessageConverter支持写出json
2.4. jackson用ObjectMapper把对象写出去

3.2、WebMvcAutoConfiguration提供几种默认HttpMessageConverters

● EnableWebMvcConfiguration通过 addDefaultHttpMessageConverters添加了默认的MessageConverter;如下:
○ ByteArrayHttpMessageConverter: 支持字节数据读写
○ StringHttpMessageConverter: 支持字符串读写
○ ResourceHttpMessageConverter:支持资源读写
○ ResourceRegionHttpMessageConverter: 支持分区资源写出
○ AllEncompassingFormHttpMessageConverter:支持表单xml/json读写
○ MappingJackson2HttpMessageConverter: 支持请求响应体Json读写
默认8个:

在这里插入图片描述
系统提供默认的MessageConverter 功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的HttpMessageConverter

六、模板引擎

● 由于 SpringBoot 使用了嵌入式 Servlet 容器。所以 JSP 默认是不能使用的。
● 如果需要服务端页面渲染,优先考虑使用 模板引擎。
在这里插入图片描述
模板引擎页面默认放在 src/main/resources/templates
SpringBoot 包含以下模板引擎的自动配置
● FreeMarker
● Groovy
● Thymeleaf
● Mustache
Thymeleaf官网:https://www.thymeleaf.org/

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<title>Good Thymes Virtual Grocery</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/gtvg.css}" />
</head>
<body>
	<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
</body
</html>

1、Thymeleaf整合

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

自动配置原理

  1. 开启了 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration 自动配置
  2. 属性绑定在 ThymeleafProperties 中,对应配置文件 spring.thymeleaf 内容
  3. 所有的模板页面默认在 classpath:/templates文件夹下
  4. 默认效果
    a. 所有的模板页面在 classpath:/templates/下面找
    b. 找后缀名为.html的页面

2、基础语法

2.1、核心用法

th:xxx:动态渲染指定的 html 标签属性值、或者th指令(遍历、判断等)
● th:text:标签体内文本值渲染
○ th:utext:不会转义,显示为html原本的样子。
● th:属性:标签指定属性渲染
● th:attr:标签任意属性渲染
● th:ifth:each…:其他th指令
● 例如:

<p th:text="${content}">原内容</p>
<a th:href="${url}">登录</a>
<img src="../../images/gtvglogo.png" 
     th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

表达式:用来动态取值
:变量取值;使用 m o d e l 共享给页面的值都直接用 {}:变量取值;使用model共享给页面的值都直接用 :变量取值;使用model共享给页面的值都直接用{}
● @{}:url路径;
● #{}:国际化消息
● ~{}:片段引用
● *{}:变量选择:需要配合th:object绑定对象

系统工具&内置对象:详细文档

● param:请求参数对象
● session:session对象
● application:application对象
● #execInfo:模板执行信息
● #messages:国际化消息
● #uris:uri/url工具
● #conversions:类型转换工具
● #dates:日期工具,是java.util.Date对象的工具类
● #calendars:类似#dates,只不过是java.util.Calendar对象的工具类
● #temporals: JDK8+ java.time API 工具类
● #numbers:数字操作工具
● #strings:字符串操作
● #objects:对象操作
● #bools:bool操作
● #arrays:array工具
● #lists:list工具
● #sets:set工具
● #maps:map工具
● #aggregates:集合聚合工具(sum、avg)
● #ids:id生成工具

2.2、语法示例

表达式:
● 变量取值:${…}
● url 取值:@{…}
● 国际化消息:#{…}
● 变量选择:*{…}
● 片段引用: ~{…}
常见:
● 文本: ‘one text’,‘another one!’,…
● 数字: 0,34,3.0,12.3,…
● 布尔:true、false
● null: null
● 变量名: one,sometext,main…
文本操作:
● 拼串: +
● 文本替换:| The name is ${name} |
布尔操作:
● 二进制运算: and,or
● 取反:!,not
比较运算:
● 比较:>,<,<=,>=(gt,lt,ge,le)
● 等值运算:==,!=(eq,ne)
条件运算:
● if-then: (if)?(then)
● if-then-else: (if)?(then):(else)
● default: (value)?:(defaultValue)
特殊语法:
● 无操作:_
所有以上都可以嵌套组合

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

3、属性设置

  1. th:href=“@{/product/list}”
  2. th:attr=“class=${active}”
  3. th:attr=“src=@{/images/gtvglogo.png},title=${logo},alt=#{logo}”
  4. th:checked=“${user.active}”
<p th:text="${content}">原内容</p>
<a th:href="${url}">登录</a>
<img src="../../images/gtvglogo.png" 
     th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

4、遍历

语法: th:each=“元素名,迭代状态 : ${集合}”

<tr th:each="prod : ${prods}">
  <td th:text="${prod.name}">Onions</td>
  <td th:text="${prod.price}">2.41</td>
  <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
  <td th:text="${prod.name}">Onions</td>
  <td th:text="${prod.price}">2.41</td>
  <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

iterStat 有以下属性:
● index:当前遍历元素的索引,从0开始
● count:当前遍历元素的索引,从1开始
● size:需要遍历元素的总数量
● current:当前正在遍历的元素对象
● even/odd:是否偶数/奇数行
● first:是否第一个元素
● last:是否最后一个元素

5、判断

th:if

<a
  href="comments.html"
  th:href="@{/product/comments(prodId=${prod.id})}"
  th:if="${not #lists.isEmpty(prod.comments)}"
  >view</a

th:switch

<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administrator</p>
  <p th:case="#{roles.manager}">User is a manager</p>
  <p th:case="*">User is some other thing</p>
</div>

6、属性优先级

● 片段
● 遍历
● 判断

<ul>
  <li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>
</ul>

在这里插入图片描述

7、行内写法

[[…]] or [(…)]

<p>Hello, [[${session.user.name}]]!</p>

8、变量选择

<div th:object="${session.user}">
  <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

等同于

<div>
  <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div

9、模板布局

● 定义模板: th:fragment
● 引用模板:~{templatename::selector}
● 插入模板:th:insert、th:replace

<footer th:fragment="copy">&copy; 2011 The Good Thymes Virtual Grocery</footer>

<body>
  <div th:insert="~{footer :: copy}"></div>
  <div th:replace="~{footer :: copy}"></div>
</body>
<body>
  结果:
  <body>
    <div>
      <footer>&copy; 2011 The Good Thymes Virtual Grocery</footer>
    </div>

    <footer>&copy; 2011 The Good Thymes Virtual Grocery</footer>
  </body>
</body>

10、devtools

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
      </dependency>

修改页面后;ctrl+F9刷新效果;
java代码的修改,如果devtools热启动了,可能会引起一些bug,难以排查

七、国际化

国际化的自动配置参照MessageSourceAutoConfiguration

实现步骤:

  1. Spring Boot 在类路径根下查找messages资源绑定文件。文件名为:messages.properties
  2. 多语言可以定义多个消息文件,命名为messages_区域代码.properties。如:
    a. messages.properties:默认
    b. messages_zh_CN.properties:中文环境
    c. messages_en_US.properties:英语环境
  3. 在程序中可以自动注入 MessageSource组件,获取国际化的配置项值
  4. 在页面中可以使用表达式 #{}获取国际化的配置项值
    @Autowired  //国际化取消息用的组件
    MessageSource messageSource;
    @GetMapping("/haha")
    public String haha(HttpServletRequest request){

        Locale locale = request.getLocale();
        //利用代码的方式获取国际化配置文件中指定的配置项的值
        String login = messageSource.getMessage("login", null, locale);
        return login;
    }

八、错误处理

1、默认机制

错误处理的自动配置都在ErrorMvcAutoConfiguration中,两大核心机制:
● 1. SpringBoot 会自适应处理错误,响应页面或JSON数据
● 2. SpringMVC的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理
在这里插入图片描述
发生错误以后,转发给/error路径,SpringBoot在底层写好一个 BasicErrorController的组件,专门处理这个请求

	@RequestMapping(produces = MediaType.TEXT_HTML_VALUE) //返回HTML
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections
			.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
	}

	@RequestMapping  //返回 ResponseEntity, JSON
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		HttpStatus status = getStatus(request);
		if (status == HttpStatus.NO_CONTENT) {
			return new ResponseEntity<>(status);
		}
		Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
		return new ResponseEntity<>(body, status);
	}

错误页面是这么解析到的

//1、解析错误的自定义视图地址
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
//2、如果解析不到错误页面的地址,默认的错误页就是 error
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);

容器中专门有一个错误视图解析器

@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean(ErrorViewResolver.class)
DefaultErrorViewResolver conventionErrorViewResolver() {
    return new DefaultErrorViewResolver(this.applicationContext, this.resources);
}

SpringBoot解析自定义错误页的默认规则

	@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}

	private ModelAndView resolve(String viewName, Map<String, Object> model) {
		String errorViewName = "error/" + viewName;
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
				this.applicationContext);
		if (provider != null) {
			return new ModelAndView(errorViewName, model);
		}
		return resolveResource(errorViewName, model);
	}

	private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
		for (String location : this.resources.getStaticLocations()) {
			try {
				Resource resource = this.applicationContext.getResource(location);
				resource = resource.createRelative(viewName + ".html");
				if (resource.exists()) {
					return new ModelAndView(new HtmlResourceView(resource), model);
				}
			}
			catch (Exception ex) {
			}
		}
		return null;
	}

容器中有一个默认的名为 error 的 view; 提供了默认白页功能

@Bean(name = "error")
@ConditionalOnMissingBean(name = "error")
public View defaultErrorView() {
    return this.defaultErrorView;
}

封装了JSON格式的错误信息

	@Bean
	@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
	public DefaultErrorAttributes errorAttributes() {
		return new DefaultErrorAttributes();
	}

规则:

  1. 解析一个错误页
    a. 如果发生了500、404、503、403 这些错误
    ⅰ. 如果有模板引擎,默认在 classpath:/templates/error/精确码.html
    ⅱ. 如果没有模板引擎,在静态资源文件夹下找 精确码.html
    b. 如果匹配不到精确码.html这些精确的错误页,就去找5xx.html,4xx.html模糊匹配
    ⅰ. 如果有模板引擎,默认在 classpath:/templates/error/5xx.html
    ⅱ. 如果没有模板引擎,在静态资源文件夹下找 5xx.html
  2. 如果模板引擎路径templates下有 error.html页面,就直接渲染

2、自定义错误响应

  1. 自定义json响应
    使用@ControllerAdvice + @ExceptionHandler 进行统一异常处理
  2. 自定义页面响应
    根据boot的错误页面规则,自定义页面模板

3、最佳实战

● 前后分离
○ 后台发生的所有错误,@ControllerAdvice + @ExceptionHandler进行统一异常处理。
● 服务端页面渲染
○ 不可预知的一些,HTTP码表示的服务器或客户端错误
■ 给classpath:/templates/error/下面,放常用精确的错误码页面。500.html,404.html
■ 给classpath:/templates/error/下面,放通用模糊匹配的错误码页面。 5xx.html,4xx.html
○ 发生业务错误
■ 核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页。
■ 通用业务,classpath:/templates/error.html页面,显示错误信息。

页面,JSON,可用的Model数据如下
在这里插入图片描述

九、嵌入式容器

Servlet容器:管理、运行Servlet组件(Servlet、Filter、Listener)的环境,一般指服务器

1、自动配置原理

● SpringBoot 默认嵌入Tomcat作为Servlet容器。
● 自动配置类是ServletWebServerFactoryAutoConfiguration,EmbeddedWebServerFactoryCustomizerAutoConfiguration
● 自动配置类开始分析功能。xxxxAutoConfiguration

@AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    
}
  1. ServletWebServerFactoryAutoConfiguration 自动配置了嵌入式容器场景
  2. 绑定了ServerProperties配置类,所有和服务器有关的配置 server
  3. ServletWebServerFactoryAutoConfiguration 导入了 嵌入式的三大服务器 Tomcat、Jetty、Undertow
    a. 导入 Tomcat、Jetty、Undertow 都有条件注解。系统中有这个类才行(也就是导了包)
    b. 默认 Tomcat配置生效。给容器中放 TomcatServletWebServerFactory
    c. 都给容器中 ServletWebServerFactory放了一个 web服务器工厂(造web服务器的)
    d. web服务器工厂 都有一个功能,getWebServer获取web服务器
    e. TomcatServletWebServerFactory 创建了 tomcat。
  4. ServletWebServerFactory 什么时候会创建 webServer出来。
  5. ServletWebServerApplicationContextioc容器,启动的时候会调用创建web服务器
  6. Spring容器刷新(启动)的时候,会预留一个时机,刷新子容器。onRefresh()
  7. refresh() 容器刷新 十二大步的刷新子容器会调用 onRefresh();

2、自定义

在这里插入图片描述
切换服务器;

<properties>
    <servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- Exclude the Tomcat dependency -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

3、最佳实战

用法:
● 修改server下的相关配置就可以修改服务器参数
● 通过给容器中放一个ServletWebServerFactory,来禁用掉SpringBoot默认放的服务器工厂,实现自定义嵌入任意服务器。

十、全面接管SpringMVC

● SpringBoot 默认配置好了 SpringMVC 的所有常用特性。
● 如果我们需要全面接管SpringMVC的所有配置并禁用默认配置,仅需要编写一个WebMvcConfigurer配置类,并标注 @EnableWebMvc 即可
● 全手动模式
○ @EnableWebMvc : 禁用默认配置
○ WebMvcConfigurer组件:定义MVC的底层行为

1、WebMvcAutoConfiguration 到底自动配置了哪些规则

SpringMVC自动配置场景给我们配置了如下所有默认行为

  1. WebMvcAutoConfigurationweb场景的自动配置类
    1.1. 支持RESTful的filter:HiddenHttpMethodFilter
    1.2. 支持非POST请求,请求体携带数据:FormContentFilter
    1.3. 导入EnableWebMvcConfiguration:
    1.3.1. RequestMappingHandlerAdapter
    1.3.2. WelcomePageHandlerMapping: 欢迎页功能支持(模板引擎目录、静态资源目录放index.html),项目访问/ 就默认展示这个页面.
    1.3.3. RequestMappingHandlerMapping:找每个请求由谁处理的映射关系
    1.3.4. ExceptionHandlerExceptionResolver:默认的异常解析器
    1.3.5. LocaleResolver:国际化解析器
    1.3.6. ThemeResolver:主题解析器
    1.3.7. FlashMapManager:临时数据共享
    1.3.8. FormattingConversionService: 数据格式化 、类型转化
    1.3.9. Validator: 数据校验JSR303提供的数据校验功能
    1.3.10. WebBindingInitializer:请求参数的封装与绑定
    1.3.11. ContentNegotiationManager:内容协商管理器
    1.4. WebMvcAutoConfigurationAdapter配置生效,它是一个WebMvcConfigurer,定义mvc底层组件
    1.4.1. 定义好 WebMvcConfigurer 底层组件默认功能;所有功能详见列表
    1.4.2. 视图解析器:InternalResourceViewResolver
    1.4.3. 视图解析器:BeanNameViewResolver,视图名(controller方法的返回值字符串)就是组件名
    1.4.4. 内容协商解析器:ContentNegotiatingViewResolver
    1.4.5. 请求上下文过滤器:RequestContextFilter: 任意位置直接获取当前请求
    1.4.6. 静态资源链规则
    1.4.7. ProblemDetailsExceptionHandler:错误详情
    1.4.7.1. SpringMVC内部场景异常被它捕获:
    1.5. 定义了MVC默认的底层行为: WebMvcConfigurer

2、@EnableWebMvc 禁用默认行为

  1. @EnableWebMvc给容器中导入 DelegatingWebMvcConfiguration组件,
    他是 WebMvcConfigurationSupport
  2. WebMvcAutoConfiguration有一个核心的条件注解, @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),容器中没有WebMvcConfigurationSupport,WebMvcAutoConfiguration才生效.
  3. @EnableWebMvc 导入 WebMvcConfigurationSupport 导致 WebMvcAutoConfiguration 失效。导致禁用了默认行为

● @EnableWebMVC 禁用了 Mvc的自动配置
● WebMvcConfigurer 定义SpringMVC底层组件的功能类

铁哥备注:
自定义SpringBoot默认MVC配置?好几个坑,这篇文章必须珍藏

观点1:@EnableWebMvc禁用行为很清楚了,本质是用了@Import直接将WebMvcConfigurationSupport导入进去了。
观点2:WebMvcAutoConfiguration.EnableWebMvcConfiguration为什么可以生效呢,EnableWebMvcConfiguration也继承了WebMvcConfigurationSupport,只不过用的是@Configuration。有大佬说是是因为先有 @EnableConfigurationProperties,优先级比较高,注册的时候判断没有WebMvcConfigurationSupport,笔者觉得这个说法很勉强。有大佬知道的可以在评论区说下。

3、WebMvcConfigurer 功能

定义扩展SpringMVC底层功能
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

十一、最佳实战

SpringBoot 已经默认配置好了Web开发场景常用功能。我们直接使用即可。

1、三种方式

方式用法用法 效果
全自动直接编写控制器逻辑全部使用自动配置默认效果
手自一体 (常用)@Configuration + 配置WebMvcConfigurer+配置 WebMvcRegistrations 不要标注@EnableWebMvc保留自动配置效果 手动设置部分功能 定义MVC底层组件
全手动@Configuration + 配置WebMvcConfigurer标注@EnableWebMvc禁用自动配置效果全手动设置

2、两种模式

1、前后分离模式: @RestController 响应JSON数据
2、前后不分离模式:@Controller + Thymeleaf模板引擎

十二、Web新特性

1、Problemdetails

RFC 7807: https://www.rfc-editor.org/rfc/rfc7807
错误信息返回新格式

原理

@Configuration(proxyBeanMethods = false)
//配置过一个属性 spring.mvc.problemdetails.enabled=true
@ConditionalOnProperty(prefix = "spring.mvc.problemdetails", name = "enabled", havingValue = "true")
static class ProblemDetailsErrorHandlingConfiguration {

    @Bean
    @ConditionalOnMissingBean(ResponseEntityExceptionHandler.class)
    ProblemDetailsExceptionHandler problemDetailsExceptionHandler() {
        return new ProblemDetailsExceptionHandler();
    }

}
  1. ProblemDetailsExceptionHandler 是一个 @ControllerAdvice集中处理系统异常
  2. 处理以下异常。如果系统出现以下异常,会被SpringBoot支持以 RFC 7807规范方式返回错误数据
	@ExceptionHandler({
			HttpRequestMethodNotSupportedException.class, //请求方式不支持
			HttpMediaTypeNotSupportedException.class,
			HttpMediaTypeNotAcceptableException.class,
			MissingPathVariableException.class,
			MissingServletRequestParameterException.class,
			MissingServletRequestPartException.class,
			ServletRequestBindingException.class,
			MethodArgumentNotValidException.class,
			NoHandlerFoundException.class,
			AsyncRequestTimeoutException.class,
			ErrorResponseException.class,
			ConversionNotSupportedException.class,
			TypeMismatchException.class,
			HttpMessageNotReadableException.class,
			HttpMessageNotWritableException.class,
			BindException.class
		})

效果:
默认响应错误的json。状态码 405

{
    "timestamp": "2023-04-18T11:13:05.515+00:00",
    "status": 405,
    "error": "Method Not Allowed",
    "trace": "org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported\r\n\tat org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:265)\r\n\tat org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:441)\r\n\tat org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:382)\r\n\tat org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:126)\r\n\tat org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:68)\r\n\tat org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:505)\r\n\tat org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1275)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1057)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:563)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:631)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:833)\r\n",
    "message": "Method 'POST' is not supported.",
    "path": "/list"
}

开启ProblemDetails返回, 使用新的MediaType

Content-Type: application/problem+json+ 额外扩展返回

在这里插入图片描述

{
    "type": "about:blank",
    "title": "Method Not Allowed",
    "status": 405,
    "detail": "Method 'POST' is not supported.",
    "instance": "/list"
}

2、函数式Web

SpringMVC 5.2 以后 允许我们使用函数式的方式,定义Web的请求处理流程。
函数式接口
Web请求处理的方式:

  1. @Controller + @RequestMapping:耦合式 (路由、业务耦合)
  2. 函数式Web:分离式(路由、业务分离)

2.1、场景

场景:User RESTful - CRUD
● GET /user/1 获取1号用户
● GET /users 获取所有用户
● POST /user 请求体携带JSON,新增一个用户
● PUT /user/1 请求体携带JSON,修改1号用户
● DELETE /user/1 删除1号用户

2.2、核心类

● RouterFunction
● RequestPredicate
● ServerRequest
● ServerResponse

2.3、示例

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.RequestPredicate;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;

import static org.springframework.web.servlet.function.RequestPredicates.accept;
import static org.springframework.web.servlet.function.RouterFunctions.route;

@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {

    private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);

    @Bean
    public RouterFunction<ServerResponse> routerFunction(MyUserHandler userHandler) {
        return route()
                .GET("/{user}", ACCEPT_JSON, userHandler::getUser)
                .GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
                .DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
                .build();
    }

}


import org.springframework.stereotype.Component;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;

@Component
public class MyUserHandler {

    public ServerResponse getUser(ServerRequest request) {
        ...
        return ServerResponse.ok().build();
    }

    public ServerResponse getUserCustomers(ServerRequest request) {
        ...
        return ServerResponse.ok().build();
    }

    public ServerResponse deleteUser(ServerRequest request) {
        ...
        return ServerResponse.ok().build();
    }

}

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

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

相关文章

自动化构建工具Gulp

第三方模块-Gulp 解释 基于node平台开发的前端构建工具 将机械化操作编写成任务&#xff0c;由一条命令来触发执行&#xff0c;提高开发效率 主要能做什么 项目上线&#xff1a;html、css、js文件的压缩合并语法转换&#xff1a;es6、less等语言的转化公共文件抽离修改文件…

替代液压比例放大器首选

比例阀放大器的选用应考虑诸多因素&#xff0c;如系统需求、放大器的兼容性、调节性、附加功能、安全特性、供应商支持和环境适应性等。 系统性能要求明确 压力与流量: 根据液压系统所需的压力和流量来确定放大器的性能指标&#xff0c;以确保比例阀能得到充分的功率支持。 …

2024/8/26 英语每日一段

Apple and Google have had nearly unchecked power over mobile apps in ways that raise prices for some of what you buy, block you from trying clever ideas, push app makers to do scuzzy things to make money, and impose Apple’s and Google’s wishes on all us.…

如何用Java SpringBoot+Vue开发高效OA办公管理系统

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

三品船舶PLM解决方案详情 三品PLM软件在船舶制造行业应用优势

自2024年起&#xff0c;船舶行业PLM&#xff08;产品全生命周期管理&#xff09;技术步入关键发展阶段。国产化工业软件领域积极倡导生态合作&#xff0c;推广统一技术底座下的合作开发模式&#xff0c;整合国内外成熟的工业软件技术与应用资源&#xff0c;旨在全面提升国内船舶…

【Bigtop】利用Bigtop3.2.0编译大数据组件RPM包

利用Bigtop3.2.0编译大数据组件RPM包 前言正文Mvn本地目录的修改FlinkKafkagrgit版本手动准备gradle的文件 前言 原文参考&#xff1a;Bigtop 从0开始 参考了上述的博文自己尝试了编译组件&#xff0c;过程还是遇到很多问题&#xff0c;一一记录&#xff0c;方便后人。 Bigt…

【方案】智慧排水系统解决方案(doc原件)

一、项目建设目标 二、项目主要内容 三、项目建设方案 1.GIS管理子系统 &#xff08;1&#xff09; 数据管理和访问 &#xff08;2&#xff09; 地图操作功能 &#xff08;3&#xff09; 地图查询定位功能 &#xff08;4&#xff09; 其他功能 2.工程管理子系统 &#xff08;1&…

免费分享!算法备案流程以及所需资料

免费分享&#xff01;算法备案流程以及所需资料 在国内&#xff0c;随着《生成式人工智能服务管理暂行办法》的出台&#xff0c;这一规定明确指出&#xff0c;任何面向中国公众提供具备舆论影响力或社会动员潜力的生成式AI服务&#xff0c;都必须经过严格的算法备案程序。 这就…

有关于算法备案的五大误区

有关于算法备案的五大误区 在这个数据为王的时代&#xff0c;算法已然成为推动社会前进的隐形巨轮。从搜索框中的每一次点击&#xff0c;到购物车里的每一件商品推荐&#xff0c;再到朋友圈里刷屏的动态&#xff0c;算法的身影无处不在&#xff0c;悄无声息地编织着我们的数字生…

云HIS系统,利用云计算平台的技术优势,建立统一的健康档案和电子存储平台,实现医疗数据共享与交换

云HIS系统源码&#xff0c;医院信息管理系统源码&#xff0c;医疗云HIS源码 基于云计算技术的B/S架构的HIS系统&#xff0c;为医疗机构提供标准化的、信息化的、可共享的医疗信息管理系统&#xff0c;实现医患事务管理和临床诊疗管理等标准医疗管理信息系统的功能。系统利用云计…

项目经理都在用的五款项目管理工具(建议收藏)

在项目管理中&#xff0c;选择合适的工具对于确保项目成功至关重要。 以下是几款备受项目经理青睐的项目管理软件&#xff1a; 1、进度猫 进度猫是一款基于云端的项目管理工具&#xff0c;以其轻量级、直观和易操作的特点受到项目经理的青睐。它提供了丰富的功能和灵活的操…

如何安装和高级 AMP for WP

当 WordPress 是支持 AMP 的 WEB 站点时&#xff0c;主要通过两个插件支持 AMP。 一个是AMP插件。 这个插件也参与谷歌的开发&#xff0c;并被确认为AMP项目的官方插件。 我最初也安装了这个AMP插件&#xff0c;但我不知道是否能够共存的常规网站和AMP兼容网站&#xff0c;很难…

这只猴子一夜赚了15亿?

通过《黑神话》思考低代码平台的发展 《黑神话》的设计分析 《黑神话》作为一款大型3A游戏&#xff0c;其优化工作主要聚焦于提升游戏性能、增强画面表现以及提升玩家体验。这包括但不限于以下几个方面&#xff1a; 技术突破&#xff1a;游戏采用了前沿的技术手段&#xff0…

AppInventor2 文本输入框(TextBox)已支持文本变更事件,非常便于实时处理输入的内容

自 v2.70开始&#xff0c;文本输入框加入了文本变更事件&#xff1a; 效果如下&#xff1a; 文本事件.gif (99.17 KB, 下载次数: 3) 下载附件 昨天 19:57 上传 同理&#xff0c;密码输入框组件也是一样的。 原文&#xff1a;AppInventor2 文本输入框&#xff08;TextBox&a…

用Python实现9大回归算法详解——08. 随机森林回归算法

1. 随机森林回归的基本概念 随机森林回归&#xff08;Random Forest Regression&#xff09;是一种集成学习方法&#xff0c;基于多棵决策树的组合来进行预测。它通过引入随机性来构建多棵独立的决策树&#xff0c;并将这些树的预测结果进行平均&#xff0c;从而提升模型的泛化…

streeapptest 工具编译看 + 测试rk3568

首先来了解一下 stressappteset 网上的资料 压力测试不就是 内存的接口测试吗&#xff1f; 网上找了些资料&#xff0c;基本没有这个工具对于 磁盘网络的测试。 我的理解&#xff0c;压力测试应该指的就是 CPU内存的测试吧。 然后是 关于这个 软件的编译。 首先是下载 git c…

避雷!Springer、Cell等出版社旗下17本SCI/SSCI被剔除,含3本on hold期刊!

2024年8月19日&#xff0c;科睿唯安本年度第八次更新Web of Science核心期刊目录。 图片来源&#xff1a;科睿唯安 与上次更新&#xff08;2024年7月&#xff09;相比&#xff0c;此次更新后的SCIE、SSCI期刊目录共17本期刊发生变动&#xff0c;详情如下&#xff1a; 图片来源…

用ComfyUI打造一键换装神器,轻松搭建本地工作流!

前言 最近快手推出了一个一键换装的模型&#xff0c;还原度还挺高的&#xff0c;效果也很不错&#xff0c;于是自己上手用ComfyUI也搭建了一套这样的工作流&#xff0c;练练手&#xff0c;搭建出来之后发现效果也还挺不错的&#xff0c;分享给大家&#xff1a; 我们先来看看快…

基于插件机制、SPI与事件驱动的系统设计

时间&#xff1a;2024年08月26日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频地址:https://xima.tv/1_F4V6FW?_sonic0 希望大家帮个忙&#xff01;如果大家有工作机会&#xff0c;希望帮小蒋内推一下&#xff0c;小蒋希…

【html+css 绚丽Loading】 000018 五行伸缩剑

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…