当前位置: 首页 > news >正文

springboot3-web开发

跟着尚硅谷学springboot3

0.配置application语法

表示复杂对象person

@Component
@ConfigurationProperties(prefix = "person")
public class Person {private String name;private Integer age;private Date birthday;private Child chlid;private List<Dog> dogs;private Map<String,Cat> cats;
}
public class Child {private String name;private Integer age;private Date birthday;private List<String> text;
}
public class Dog {private String name;private Integer age;
}
public class Cat {private String name;private Integer age;
}

properties写法

person.name=张三
person.age=18
person.birthday=2022/01/01 00:01:01
person.like=true
person.child.name=李四
person.child.age=18
person.child.birthday=2023/02/02 02:02:02
person.child.text[0]=aaa
person.child.text[1]=bbb
person.dogs[0].name=小红
person.dogs[0].age=3
person.dogs[1].name=小橙
person.dogs[1].age=5
person.cats.cat1.name=小绿//cat1是key
person.cats.cat1.age=2
person.cats.cat2.name=小蓝
person.cats.cat2.age=7

yml写法

person:name: 张三age: 18birthday: 2023/12/12 12:12:12like: truechild:name: 李四age: 19birthday: 2023/01/01
#    text: ["aaa","bbb"]text:- aaa- bbbdogs:- name: 小黑age: 12- name: 小白age: 5cats:
#    cat1: {name: 小蓝,age: 2}cat1:name: 小蓝age: 2cat2:name: 小红age: 3

细节:

  • yml中birthDay推荐写成birth-day
  • 文本
    ‘张三 \n’ 单引号不会转义
    "李四 \n"双引号会转义
  • 大文本
    |开头,大文本写在下层,保留文本格式,换行符正确显示
    >开头,大文本写在下层,折叠换行符
  • 多文档合并
    使用---可以把多个yaml文档合并在一个文档中,每个文档区依然认为内容独立

1.web场景

1.1. 自动装配

  1. 整合web场景
 		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
  1. 引入了autoconfigure 功能
  2. @EnableAutoConfiguration注解使用 @Import(AutoConfigurationImportSelector.class)批量导入组件
  3. 加载 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
    文件中配置的所有组件
  4. 所有配置如下:
org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration
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
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
  1. 绑定了配置文件的一堆配置项
  • SpringMVC的所有配置spring.mvc
  • Web场景通用配置spring.web
  • 文件上传配置spring.servlet.multipart
  • 服务器的配置 server:比如:编码方式

1.2.默认配置

默认配置:

  1. 包含了 ContentNegotiatingViewResolverBeanNameViewResolver 组件,方便视图解析
  2. 默认的静态资源处理机制: 静态资源放在static文件夹下即可直接访问
  3. 自动注册Converter,GenericConverter,Formatter组件,适配常见的数据类型转换和格式化需求
  4. 支持HttpMessageConverters可以方便返回json数据类型
  5. 注册MessageCodesResolver,方便国际化及错误消息处理
  6. 支持静态 index.html
  7. 自动使用ConfigurableWebBindinglnitializer,实现消息处理、数据绑定、类型转化等功能

重要:

  • 如果想保持boot mv的默认配置,并且自定义更多的mvc配置,如: interceptors,formatters,view controllers 等。可以使用@Configuration注解添加一个 WebMvcConfigurer 类型的配置类并不要标注 @EnableWebMvc(这个会使自动配置失效)
  • 如果想保持bootmvc的默认配置,但要自定义核心组件实例,比如:RequestMappingHandlerMapping, RequestMappingHandlerAdapter ExceptionHandlerExceptionResolver,给容器中放一个 WebMvcRegistrations 组件即
  • 如果想全面接管Spring MVC,@Configuration 标注一个配置类,并加上 @EnableWebMvc注解,实现 WebMvcConfiqurer 接口

2. 静态资源

2.0.WebMvcAutoConfiguration原理

2.0.1.生效条件
@AutoConfiguration(after = {DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class}
)//在这些配置之后
@ConditionalOnWebApplication(type = Type.SERVLET
)//是web应用就生效,类型SERVLET
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})//有这些类生效
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})//没有bean生效,默认没有
@AutoConfigureOrder(-2147483638)
@ImportRuntimeHints({WebResourcesRuntimeHints.class})
public class WebMvcAutoConfiguration {
2.0.2.效果

1.放了两个Filter:

  • HiddenHttpMethodFilter;页面表单提交Rest请求(GET、POST、PUT、DELETE)
  • FormContentFilter : 表单内容Fiter,GET (数据放URL后面)、POST (数据放请求体)请求可以携带数据,PUT、DELETE的请求体数据会被忽略

2.给容器中放了 webMvcConfigurer 组件;给SpringMVC添加各种定制功能

  • 所有的功能会和配置文件绑定
  • WebMvcProperties:spring.mvc配置文件
  • WebProperties:spring.web配置文件
  @Configuration(proxyBeanMethods = false)@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})@Order(0)public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
2.0.3.WebMvcConfigurer接口

提供了配置SpringMVC底层的所有组件

public interface WebMvcConfigurer {default void configurePathMatch(PathMatchConfigurer configurer) {}//路径匹配default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}//内容协商default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}//异步支持default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}//默认请求,默认接受:/default void addFormatters(FormatterRegistry registry) {}//格式化器default void addInterceptors(InterceptorRegistry registry) {}//拦截器default void addResourceHandlers(ResourceHandlerRegistry registry) {}//添加资源处理器:处理静态资源规则default void addCorsMappings(CorsRegistry registry) {}//跨域default void addViewControllers(ViewControllerRegistry registry) {}//视图控制器:default void configureViewResolvers(ViewResolverRegistry registry) {}//视图解析default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}//参数解析器default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}//返回值处理器default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}//消息转换器default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}//扩展:消息转换default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}//配置异常解析器default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}//扩展:异常解析器@Nullabledefault Validator getValidator() {return null;}@Nullabledefault MessageCodesResolver getMessageCodesResolver() {return null;}
}
2.0.4.静态资源规则
public void addResourceHandlers(ResourceHandlerRegistry registry) {if (!this.resourceProperties.isAddMappings()) {logger.debug("Default resource handling disabled");} else {this.addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(), "classpath:/META-INF/resources/webjars/");this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {registration.addResourceLocations(this.resourceProperties.getStaticLocations());if (this.servletContext != null) {ServletContextResource resource = new ServletContextResource(this.servletContext, "/");registration.addResourceLocations(new Resource[]{resource});}});}}
  1. 规则一: 访问:/webjars/**路径就去 classpath:/META-INF/resources/webjars/ 下找资源
  2. 规则二:访问:/**路径就去静态资源默认的四个位置找资源
  • "classpath:/META-INF/resources/",
  • "classpath:/resources/",
  • "classpath:/static/",
  • "classpath:/public/"
  1. 规则三:静态资源默认都有缓存规则的设置
  • 所有缓存的设置,直接通过配置文件:spring.web
  • CachePeriod 缓存周期,多久不用找服务器要新的。默认没有,以S为单位
  • CacheControl:http缓存
  • UseLastModified:是否使用最后一次修改。配合HTTP Cache规则
 registration.setCachePeriod(this.getSeconds(this.resourceProperties.getCache().getPeriod()));registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());  registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());

4.EnableWebMvcConfiguration

//springBoot 给容器中放 webMvcConfigurationSupport 组件。
//我们如果自己放了 webMvcConfigurationSupport 组件,Boot的webMvcAutoConfiguration都会失效。@Configuration(proxyBeanMethods = false)@EnableConfigurationProperties({WebProperties.class})public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
2.0.5.为什们容器中放一个WebMvcConfigurer就能配置底层行为
  1. WebMvcAutoConfiguration 是一个自动配置类,它里面有一个 EnablewebMvcConfiguration
  2. EnableWebMvcConfiguration 继承与 DelegatingwebMvcConfiguration ,这两个都生效
  3. DelegatingWebMvcConfiguration 利用DI把容器中所有 WebMvcConfigurer 注入进来
  4. 别人调用DelegatingWebMvcConfiguration的方法配置底层规则,而它调用所有 WebMvcConfigurer的配置底层方法。

2.1.默认规则

2.1.1.静态资源映射

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

  1. /webjars/** 的所有路径资源都在 classpath:/META-INF/resources/webjars/
  2. /**的所有路径资源都在 classpath:/META-INF/resources/、classpath:/resources/、classpath:/static/、classpath:/public/
  3. 所有静态资源都定义了缓存规则。[浏览器访问过一次,就会缓存一段时间],但此功能参数无默认值
    a.period:缓存间隔。默认 0S;
    b.cacheControl: 缓存控制。默认无:
    c.useLastModified: 是否使用lastModified头。默认false;
2.1.2.静态资源缓存

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

2.1.3.欢迎页

欢迎页规则在WebMvcAutoConfiguration中进行了定义:
1.在静态资源目录下找index.html
2.没有就在templates下找index模板页

2.1.4.Favicon

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

2.1.5.缓存实验
#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.2.自定义静态资源规则

2.2.1.配置方式

spring.mvc :静态资源访问前缀路径
spring.web

  • 静态资源目录
  • 静态资源缓存策略
2.2.2.代码方式

容器中只要有一个 WebMvcConfigurer 组件。配置的底层行为都会生效。
@EnableWebMvc 禁用boot的默认配置

@Configuration
public class MyConfig implements WebMvcConfigurer {//静态资源@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {//保留WebMvcConfigurer.super.addResourceHandlers(registry);//自己配置规则registry.addResourceHandler("/static/**").addResourceLocations("classpath:/a/","classpath:/b/").setCacheControl(CacheControl.maxAge(7200, TimeUnit.SECONDS));}
}

3.路径匹配

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

3.1.Ant 风格路径用法

Ant风格的路径模式语法具有以下规则:

  • *表示任意数量的字符。
  • ?表示任意一个字符
  • **表示任意数量的目录
  • {}:表示一个命名的模式占位符
  • []表示字符集合,例如[a-z]表示小写字母。

例如:

  • *.html 匹配任意名称,扩展名为.html的文件。
  • /folder1/*/*java 匹配在folder1目录下的任意两级目录下的.java文件。
  • /folder2/**/*jsp 匹配在folder2目录下任意目录深度的.jsp文件。
  • /type]/id;.html 匹配任意文件名为{id}.html,在任意命名的{type}目录下的文件。
    注意:Ant风格的路径模式语法中的特殊字符需要转义,如:
  • 要匹配文件路径中的星号,则需要转义为\\*
  • 要匹配文件路径中的问号,则需要转义为\\?
3.2.模式切换

AntPathMatcherPathPatternParser

  • PathPatternParser 在jmh 基准测试下,有6~8 倍吞吐量提升,降低30%~40%空间分配率
  • PathPatternParser 兼容 AntPathMatcher语法,并支持更多类型的路径模式
  • PathPatternParser "**" 多段匹配的支持仅允许在模式末尾使用
    默认使用PathPatternParser
    在这里插入图片描述
    需要修改成ant风格
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

4.内容协商

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

4.1.多端内容适配

4.1.1. 默认规则
  1. SpringBoot 多端内容适配
  • 1.1.基于请求头内容协商:(默认开启)
    客户端向服务端发送请求,携带HTTP标准的Accept请求头
    1.1.1.Accept: application/jsontext/xmltext/yaml
    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类型数据
4.1.2.效果演示(返回json和xml)

请求同一个接口,可以返回json和xml不同格式数据(@RestController默认返回json)

  1. 引入支持xml内容依赖
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
</dependency>
  1. 注解
    @JacksonXmIRootElement //可以写出为xml文档
  2. 开启基于请求参数的内容协商
#开启基于请求参数的协商功能,默认名format
spring.mvc.contentnegotiation.favor-parameter=true
#设置请求参数名,默认名format
spring.mvc.contentnegotiation.parameter-name=type

4.2.自定义内容返回

4.2.1.增加yaml返回支持
  • 导入依赖
		<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-yaml</artifactId></dependency>
  • 编写配置
#新增一种媒体类型
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
  • 使用方法
    @Testvoid test() throws JsonProcessingException {Person person = new Person();person.setName("张三");person.setAge(18);YAMLFactory yamlFactory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);ObjectMapper mapper = new ObjectMapper(yamlFactory);String s = mapper.writeValueAsString(person);System.out.println(s);}
  • 增加HttpMessageConverter 组件,专门负责把对象写出为yaml格式
    • 重写WebMvcConfigurer接口中的extendMessageConverters方法
    @Override //配置一个能把对象转为yaml的messageConverter
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {WebMvcConfigurer.super.extendMessageConverters(converters);converters.add(new MyYamlHttpMessageConverter());
    }
    
    • 自定义我的HttpMessageConverter
    public class MyYamlHttpMessageConverter extends AbstractHttpMessageConverter<Object>{private ObjectMapper mapper = null;public MyYamlHttpMessageConverter() {//告诉SpringBoot这个MessageConverter支持哪种媒体类型super(new MediaType("text", "yaml", Charset.forName("UTF-8")));YAMLFactory yamlFactory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);this.mapper = new ObjectMapper(yamlFactory);}@Overrideprotected boolean supports(Class<?> clazz) {//只要是对象类型就支持return true;}@Override //@RequestBodyprotected 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 outputStream = outputMessage.getBody()){this.mapper.writeValue(outputStream,methodReturnValue);}}
    }
    
4.2.2.如何增加其他
  • 配置媒体类型支持:spring.mvc.contentnegotiation.media-types.yaml=text/yaml
  • 编写对应的HttpMessageConverter,自定义
  • 把HttpMessageConverter加入到底层,重写WebMvcConfigurer接口中的extendMessageConverters方法

4.3.内容协商原理-HttpMessageConverter

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

4.3.1.@ResponseBody HttpMessageConverter 处理

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

  1. 如果controller方法的返回值标注了@ResponseBody注解
  • a.请求进来先来到 DispatcherServletdoDispatch()进行处理

  • b.找到一个HandlerAdapter 适配器。利用适配器执行目标方法

  • c.RequestMappingHandlerAdapter 来执行,调用 invokeHandlerMethod() 来执行目标方法

  • d.目标方法执行之前,准备好两个东西

      1.HandlerMethodArgumentResolver:参数解析器,确定目标方法每个参数值2.HandlerMethodReturnValueHandler: 返回值处理器,确定目标方法的返回值该怎么处理
    
  • e.RequestMappingHandlerAdapter 里面的 invokeAndHandle()真正执行目标方法

  • f.目标方法执行完成,会返回返回值对象

  • g.找到一个合适的返回值处理器 HandlerMethodReturnValueHandler

  • h.最终找到 RequestResponseBodyMethodProcessor 能处理标注了 @ResponseBody 注解的方法- i.RequestResponseBodyMethodProcessor调用 writewithMessageConverters 利用 MessageConverter把返回值写出去

  1. HttpMessageConverter会先进行内容协商
  • 遍历所有的MessageConverter看谁支持这种内容类型的数据

  • 默认 MessageConverter 有以下:
    在这里插入图片描述

  • 最终因为要 json所以MappingJackson2HttpMessageConverter 支持写出json

  • jackson用0bjectMapper 把对象写出去

4.3.2.WebMvcAutoConfiguration 提供几种默认 HttpMessagonverters
  • EnableWebMvcConfiguration通过addDefaultHttpMessageConverters 添加了默认的MessageConv erter;如下:
    • ByteArrayHttpMessageConverter : 支持字节数据读写
    • StringHttpMessageConverter :支持字符串读写
    • ResourceHttpMessageConverter :支持资源读写
    • ResourceRegionHttpMessageConverter:支持分区资源写出
    • AllEncompassingFormHttpMessageConverter:支持表单xml/json读写
    • MappingJackson2HttpMessageConverter :支持请求响应体Json读写
      默认8个:
      在这里插入图片描述
      系统提供默认的MessageConverter 功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的 HttpMessageConverter

4.4.WebMvcConfigurationSupport

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

 static {ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);jaxb2Present = ClassUtils.isPresent("jakarta.xml.bind.Binder", classLoader);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);jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader);kotlinSerializationCborPresent = ClassUtils.isPresent("kotlinx.serialization.cbor.Cbor", classLoader);kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader);kotlinSerializationProtobufPresent = ClassUtils.isPresent("kotlinx.serialization.protobuf.ProtoBuf", classLoader);}

5.模版引擎

  • 由于SpringBoot 使用了嵌入式Servlet容器。所以JSP 默认是不能使用的。
  • 如果需要服务端页面渲染,优先考虑使用 模板引擎
    在这里插入图片描述
    模板引擎页面默认放在src/main/resources/templates
    SpringBoot包含以下模板引擎的自动配置
  • FreeMarker
  • Groovy
  • Thymeleaf
  • Mustache
    Thymeleaf官网
<!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" href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" /></head><body><p th:text="#{home.welcome}">Welcome to our grocery store!</p></body></html>

5.1Thymeleaf整合

<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的页面
    使用示例
//@RestController //前后端分离
@Controller //适配 服务端渲染 前后端不分离模式
public class WelcomeController {@GetMapping("/well")public String  hello(@RequestParam("name")String name, Model model){//模板的逻辑视图名//物理视图 = 前缀 + 逻辑视图名 + 后缀//真实地址 = classpath:/templates/welcome.html//把需要给页面共享的数据放在model中model.addAttribute("msg",name);return "welcome";}
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>哈哈:<span th:text="${msg}"></span></h1>
</body>
</html>

5.2 Thymeleaf基础语法

5.2.1 核心用法

th:xxx 动态渲染指定的 html 标属性值、或者th指令 (遍历、判断等)

  • th:text : 标签体内文本值渲染
    • th:utext :不会转义,显示为html原本的样子
  • th:属性:标签指定属性染
  • th:attr:标签任意属性渲染
  • th:if th: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}/>

表达式:用来动态取值

  • ${} :变量取值;使用model共享给页面的值都直接用${}
  • @{} :url 路径
  • #{}:国际化消息
  • ~{}:片段引用
  • *{}:变量选择:需要配合th:object绑定对象

系统工具&内置对象

  • param:请求参数对象
  • session: session对象
  • application:application对象
  • #execInfo:模板执行信息
  • #messages:国际化消息
  • #uris : uri/url工具
  • #conversions:类型转换工具
  • #dates:日期工具,是java.uti.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生成工具
5.2.2 语法示例

表达式:

  • 变量取值:${}
  • url 取值:@{}
  • 国际化消息: #{...}
  • 变量选择:*{...}
  • 片段引用:~{…}

文本操作:

  • 拼串:+
  • 文本拼接:| The name is ${name} |

布尔操作:

  • 二进制运算:and ,or
  • 取反 :! , not

比较运算:

  • 比较:>,<,>=,<=(gt,lt,ge,le)
  • 等值运算 : ==!=eqne

条件运算:

  • if-then:(if)?(then)
  • if-then-else:(if)?(then):(else)
  • default:(value)?:(defaultValue)

5.3 属性设置

1.th:herf="@{/product/list}"
2.th:attr="class=${active}"
3.th:attr="src=@{/image/1.png},title=${logo},alt=#{logo}
4.th:checked="${user.active}"

5.4遍历

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

<tr th:each="prod : ${prods}"><td th:text="${prod.nane}">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="sprod.pricel">2.41</td><td th:text="${prod.instock}? #{true} : #{false}">yes</td>
</tr>

iterStat 有以下属性

  • index:当前遍历元素的索引,从0开始。
  • cqunt:当前遍历元素的索引,从1开始
  • size:需要遍历元素的总数量
  • current:当前正在遍历的元素对象
  • even/odd:是否偶数/奇数行
  • first:是否第一个元素
  • last:是否最后一个元素

使用示例

@JacksonXmlRootElement //可以写成为xml文档
@Data
@NoArgsConstructor
@AllArgsConstructor
public class People {private Long id;private String userName;private String email;private Integer age;private String role;
}
@Controller //适配 服务端渲染 前后端不分离模式
public class WelcomeController {@GetMapping("/list")public String  list(Model model){List<People> peoples = Arrays.asList(new People(1L,"张三1","1@qq.com",1,"pm"),new People(2L,"张三2","2@qq.com",2,"admin"),new People(3L,"张三3","3@qq.com",3,"hr"));model.addAttribute("peoples",peoples);return "list";}
}

list.html bootstrap文档

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>列表</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
</head>
<body>
<table class="table"><thead><tr><th scope="col">ID</th><th scope="col">名字</th><th scope="col">邮箱</th><th scope="col">年龄</th><th scope="col">角色</th><th scope="col">状态信息</th></tr></thead><tbody><tr th:each="people,state:${peoples}"><th scope="row" th:text="${people.id}">1</th><td th:text="${people.userName}">Mark</td><td >[[${people.email}]]</td><td >[[${people.age}]]</td><td th:text="${people.role}">Mark</td><td>index:[[${state.index}]] <br/>count:[[${state.count}]] <br/>size:[[${state.size}]] <br/>current:[[${state.current}]] <br/>event(true)/odd(false):[[${state.even}]] <br/></td></tr></tbody>
</table>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</body>
</html>

5.5判断

th:if th:switch

<td th:if="${#strings.isEmpty(people.email)}" th:text="'联系我'"></td><td th:if="${not #strings.isEmpty(people.email)}" th:text="${people.email}"></td><td th:if="${people.age}>2" th:text="|${people.age} /满月了|"></td><td th:if="${people.age}<=2" th:text="|${people.age} /没满月|"></td><td th:switch="${people.role}"><button th:case="'admin'" type="button" class="btn btn-primary">管理员</button><button th:case="'pm'" type="button" class="btn btn-secondary">经理</button><button th:case="'hr'" type="button" class="btn btn-success">人事</button></td>

5.6属性优先级

在这里插入图片描述

5.7 行内写法

 <td >[[${people.email}]]</td><td >[[${people.age}]]</td><td th:text="${people.role}">Mark</td><td>index:[[${state.index}]] <br/>count:[[${state.count}]] <br/>size:[[${state.size}]] <br/>current:[[${state.current}]] <br/>event(true)/odd(false):[[${state.even}]] <br/></td>

5.8 变量选择

  • *{}:变量选择:需要配合th:object绑定对象
<div th:object="${session.user}">
<p>Name:<span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastilame}">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>

5.9 模板布局

  • 定义模板:th:fragment
  • 引用模板~{templatename::selector}
  • 插入模板:th:insert th:replace
<div th:insert="~{templatename::selector}"></div>
<div th:replace="~{templatename::selector}"></div>

5.10devtools

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

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

6.国际化

国际化的自动配置参照 MessageSourceAutoConfiguration
实现步骤:

  1. Spring Boot 在类路径根下查找messages资源绑定文件。文件名为: messages.properties
  2. 多语言可以定义多个消息文件,命名为 messages_区域代码.properties 。如:
  • messages.properties:默认
  • messageszh CN.properties:中文环境
  • messagesen US.properties :英语环境
  1. 在程序中可以自动注入 MessageSource 组件,获取国际化的配置项值
  2. 在页面中可以使用表达式 #{}获取国际化的配置项值

代码获取国际化配置的值
在这里插入图片描述

7.错误处理

7.1 默认机制

错误处理的自动配置都在 ErrorMvcAutoConfiguration 中,两大核心机制:

  • SpringBoot 会自适应处理错误响应页面JSON数据
  • SpringMVC的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理
    在这里插入图片描述
  • 发生错误以后,转发给/error路径,SpringBoot在底层写好一个BasicErrorController的组件,专门处理这个请求
   @RequestMapping(produces = {"text/html"})public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = this.getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));response.setStatus(status.value());ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);return modelAndView != null ? modelAndView : new ModelAndView("error", model);}@RequestMappingpublic ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {HttpStatus status = this.getStatus(request);if (status == HttpStatus.NO_CONTENT) {return new ResponseEntity(status);} else {Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));return new ResponseEntity(body, status);}}
  • 错误页面解析
//1、解析自定义的错误的视图地址ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);//2、如果解析不到错误页面的地址,默认的错误页就是 errorreturn modelAndView != null ? modelAndView : new ModelAndView("error", model);

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

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

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

 public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {modelAndView = this.resolve((String)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);return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);}private ModelAndView resolveResource(String viewName, Map<String, Object> model) {String[] var3 = this.resources.getStaticLocations();int var4 = var3.length;for(int var5 = 0; var5 < var4; ++var5) {String location = var3[var5];try {Resource resource = this.applicationContext.getResource(location);resource = resource.createRelative(viewName + ".html");if (resource.exists()) {return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);}} catch (Exception var8) {}}return null;}

容器中有一个默认的名为error的view,默认白页功能。

 @Bean(name = {"error"})@ConditionalOnMissingBean(name = {"error"})public View defaultErrorView() {return this.defaultErrorView;}
@Bean@ConditionalOnMissingBean(value = {ErrorAttributes.class},search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();}

规则:

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

7.2 自定义错误响应

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

7.3 最佳实战

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

8.嵌入式容器

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

8.1.自动配置原理

  • SpringBoot 默认嵌入Tomcat作为Servlet容器
  • 自动配类是ServletWebServerFactoryAutoConfiguration EmbeddedWebServerFactoryCustomizerAutoConfiguration
  • 自动配置类开始分析功能。xxxxAutoConfiguration
@AutoConfiguration(after = {SslAutoConfiguration.class}
)
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {

原理:

  1. ServletWebServerFactoryAutoConfiguration 自动配置了嵌入式容器场景
  2. 绑定了ServerProperties 配置类,所有和服务器有关的配置server
  3. ServletWebServerFactoryAutoConfiguration 导入了嵌入式的三大服务器 TomcatJettyUndertow
    • 导入 Tomcat、Jetty、 Undertow 都有条件注解。系统中有这个类才行(也就是导了包)
    • 默认 Tomcat 配置生效。给容器中放TomcatServletWebServerFactory
     @Configuration(proxyBeanMethods = false
    )
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(value = {ServletWebServerFactory.class},search = SearchStrategy.CURRENT
    )
    static class EmbeddedTomcat {EmbeddedTomcat() {}@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().toList());factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().toList());factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().toList());return factory;}
    }
    
  • 都给容器中 ServletWebServerFactory 放了一个 web服务器工厂(造web服务器的)
  • web服务器工厂都有一个功能getWebServer 获取web服务
	@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());for (LifecycleListener listener : this.serverLifecycleListeners) {tomcat.getServer().addLifecycleListener(listener);}Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}
  • TomcatServletWebServerFactory 创建了tomcat
  1. ServletWebServerFactory 什么时候会创建 webServer出来。

  2. ServletWebServerApplicationContext ioc容器,启动的时候会调用创建web服务器
    在这里插入图片描述

  3. Spring**容器刷新(启动)**的时候,会预留一个时机,刷新子容器。 onRefresh()

@Overrideprotected void onRefresh() {super.onRefresh();try {createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}
  1. refresh()容器刷新十二大步的刷新子容器会调用 onRefresh();

用法:

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

9.全面接管SpringMVC

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

9.1 WebMvcAutoConfiguration 自动配置了那些规则

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

9.1.1 WebMvcAutoConfiguration web场景的自动配置类
  1. 支持RESTful的filter: HiddenHttpMethodFilter
  2. 支持非POST请求,请求体携带数据: FormContentFilter
  3. 导入 EnableWebMvcConfiguration:
    • RequestMappingHandlerAdapter
    • welcomePageHandlerMapping : 欢迎页功能支持(模板引擎目录、静态资源目录放index.html),项目访问/就默认展示这个页面
    • RequestMappingHandlerMapping:找每个请求由谁处理的映射关系
    • ExceptionHandlerExceptionResolver:默认的异常解析器
    • LocaleResolver :国际化解析器
    • ThemeResolver:主题解析器
    • FlashMapManager:临时数据共享
    • FormattingConversionService :数据格式化、类型转化
    • Validator:数据校验JSR303提供的数据校验功能
    • WebBindingInitializer:请求参数的封装与绑定
    • ContentNegotiationManager:内容协商管理器
  4. WebMvcAutoConfigurationAdapter配置生效,它是一个 WebMvcConfigurer,定义mvc底层组件.
    • 定义好WebMvcConfigurer 底层组件默认功能
    • 视图解析器:InternalResourceViewResolver
    • 视图解析器:BeanNameViewResolver 视图名 (controller方法的返回值字符串)就是组件名
    • 内容协商解析器:ContentNegotiatingViewResolver
    • 请求上下文过滤器: RequestContextFilter:任意位置直接获取当前请求
//当前请求路径ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();//任意位置随时通过RequestContextHolder 获取当前请求和响应的信息HttpServletRequest request = attributes.getRequest();HttpServletResponse response = attributes.getResponse();StringBuffer requestURL = request.getRequestURL();
- 静态资源链规则
- `ProblemDetailsExceptionHandler`:错误详情.- SpringMVC内部场景异常被它捕获
  1. 定义了MVC默认的底层行为: WebMvcConfigurer

9.1.2 @EnableWebMvc禁用默认行为

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

9.1.3 WebMvcConfigurer 功能

public interface WebMvcConfigurer {//路径配:自定义URL路径匹配。可以自动为所有路径加上指定前缀,比如 /apidefault void configurePathMatch(PathMatchConfigurer configurer) {}
//内容协商:支持多种数据格式返回。需要配合支持这种类型的HttpMessageConverterdefault void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}//异步支持:default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}//默认Servlet:可以覆盖 Tomcat的DefaultServlet。让DispatcherServlet拦截/default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}//功能:格式化器:支持属性上@NumberFormat和@DatetimeFormat的数据类型转换//默认:GenericConversionServicedefault void addFormatters(FormatterRegistry registry) {}//拦截器:拦截收到的所有请求default void addInterceptors(InterceptorRegistry registry) {}//静态资源处理:静态资原路径映射、缓存控制//ResourceHandlerRegistrydefault void addResourceHandlers(ResourceHandlerRegistry registry) {}// 跨域default void addCorsMappings(CorsRegistry registry) {}// 视映射:直接将请求路径与物理视图映射用于无java业务逻辑的直接视图页渲染default void addViewControllers(ViewControllerRegistry registry) {}//视图解析器:逻辑视图转为物理视图//ViewResolverCompositedefault void configureViewResolvers(ViewResolverRegistry registry) {}//参数解析器:default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}// 返回值解析器:default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}
//消息转换器:标注@ResponseBody的返回值会利用MessageConverter直接写出去
// 8 个,支持byte,string,multipart,resource,jsondefault void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}// 异常处理器:default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}//功能:数据校验:校验Controller 上使用@Valid标注的参数合法性。需要导入starter-validator@Nullabledefault Validator getValidator() {return null;}//消息码解析器:国际化使用@Nullabledefault MessageCodesResolver getMessageCodesResolver() {return null;}
}

10.最佳实践(三种方式 两种模式)

在这里插入图片描述
两种模式
1、前后分离模式: @RestController 响应JSON数据
2、前后不分离模式: @Controller + Thymeleaf模板引擎

11.web新特性

11.1 Problemdetails

    @Configuration(proxyBeanMethods = false)@ConditionalOnProperty(prefix = "spring.mvc.problemdetails",name = {"enabled"},havingValue = "true")static class ProblemDetailsErrorHandlingConfiguration {ProblemDetailsErrorHandlingConfiguration() {}@Bean@ConditionalOnMissingBean({ResponseEntityExceptionHandler.class})@Order(0)ProblemDetailsExceptionHandler problemDetailsExceptionHandler() {return new ProblemDetailsExceptionHandler();}}

1.ProblemDetailsExceptionHandler 是一个 @ControllerAdvice 集中处理系统异常
2.处理以下异常。如果系统出现以下异常,会被SpringBoot支持以 RFC7807 规范方式返回错误数据

  @ExceptionHandler({HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class, HttpMediaTypeNotAcceptableException.class, MissingPathVariableException.class, MissingServletRequestParameterException.class, MissingServletRequestPartException.class, ServletRequestBindingException.class, MethodArgumentNotValidException.class, HandlerMethodValidationException.class, NoHandlerFoundException.class, NoResourceFoundException.class, AsyncRequestTimeoutException.class, ErrorResponseException.class, MaxUploadSizeExceededException.class, ConversionNotSupportedException.class, TypeMismatchException.class, HttpMessageNotReadableException.class, HttpMessageNotWritableException.class, MethodValidationException.class, BindException.class})@Nullablepublic final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {if (ex instanceof HttpRequestMethodNotSupportedException) {HttpRequestMethodNotSupportedException subEx = (HttpRequestMethodNotSupportedException)ex;return this.handleHttpRequestMethodNotSupported(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof HttpMediaTypeNotSupportedException) {HttpMediaTypeNotSupportedException subEx = (HttpMediaTypeNotSupportedException)ex;return this.handleHttpMediaTypeNotSupported(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof HttpMediaTypeNotAcceptableException) {HttpMediaTypeNotAcceptableException subEx = (HttpMediaTypeNotAcceptableException)ex;return this.handleHttpMediaTypeNotAcceptable(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof MissingPathVariableException) {MissingPathVariableException subEx = (MissingPathVariableException)ex;return this.handleMissingPathVariable(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof MissingServletRequestParameterException) {MissingServletRequestParameterException subEx = (MissingServletRequestParameterException)ex;return this.handleMissingServletRequestParameter(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof MissingServletRequestPartException) {MissingServletRequestPartException subEx = (MissingServletRequestPartException)ex;return this.handleMissingServletRequestPart(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof ServletRequestBindingException) {ServletRequestBindingException subEx = (ServletRequestBindingException)ex;return this.handleServletRequestBindingException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof MethodArgumentNotValidException) {MethodArgumentNotValidException subEx = (MethodArgumentNotValidException)ex;return this.handleMethodArgumentNotValid(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof HandlerMethodValidationException) {HandlerMethodValidationException subEx = (HandlerMethodValidationException)ex;return this.handleHandlerMethodValidationException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof NoHandlerFoundException) {NoHandlerFoundException subEx = (NoHandlerFoundException)ex;return this.handleNoHandlerFoundException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof NoResourceFoundException) {NoResourceFoundException subEx = (NoResourceFoundException)ex;return this.handleNoResourceFoundException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof AsyncRequestTimeoutException) {AsyncRequestTimeoutException subEx = (AsyncRequestTimeoutException)ex;return this.handleAsyncRequestTimeoutException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof ErrorResponseException) {ErrorResponseException subEx = (ErrorResponseException)ex;return this.handleErrorResponseException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else if (ex instanceof MaxUploadSizeExceededException) {MaxUploadSizeExceededException subEx = (MaxUploadSizeExceededException)ex;return this.handleMaxUploadSizeExceededException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);} else {HttpHeaders headers = new HttpHeaders();if (ex instanceof ConversionNotSupportedException) {ConversionNotSupportedException theEx = (ConversionNotSupportedException)ex;return this.handleConversionNotSupported(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);} else if (ex instanceof TypeMismatchException) {TypeMismatchException theEx = (TypeMismatchException)ex;return this.handleTypeMismatch(theEx, headers, HttpStatus.BAD_REQUEST, request);} else if (ex instanceof HttpMessageNotReadableException) {HttpMessageNotReadableException theEx = (HttpMessageNotReadableException)ex;return this.handleHttpMessageNotReadable(theEx, headers, HttpStatus.BAD_REQUEST, request);} else if (ex instanceof HttpMessageNotWritableException) {HttpMessageNotWritableException theEx = (HttpMessageNotWritableException)ex;return this.handleHttpMessageNotWritable(theEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);} else if (ex instanceof MethodValidationException) {MethodValidationException subEx = (MethodValidationException)ex;return this.handleMethodValidationException(subEx, headers, HttpStatus.INTERNAL_SERVER_ERROR, request);} else if (ex instanceof BindException) {BindException theEx = (BindException)ex;return this.handleBindException(theEx, headers, HttpStatus.BAD_REQUEST, request);} else {throw ex;}}}

默认关闭: spring.mvc.problemdetails.enabled=true
开启后:Content-Type:application/problem+json

11.2 函数式web

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

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

函数式Web示例

  • 1、给容器中放一个Bean:类型是 RouterFunction
  • 2、每个业务准备一个自己的handler
@Configuration
public class WebFunctionConfig {/*** 函数式web* 1、给容器中放一个Bean:类型是 RouterFunction<ServerResponse>* 2、每个业务准备一个自己的handler*  核心四大对象* 1、RouterFunction:定义路由信息。发什么请求,谁来处理* 2、RequestPredicate:定义请求:请求谓语。请求方式(GET、POST)、请求参数* 3、ServerRequest:封装请求完整数据* 4、ServerResponse:封装响收完整数据*/@Beanpublic RouterFunction<ServerResponse> userRouter(UserBizHandler userBizHandler /* 自动注入*/){return RouterFunctions.route() //开始定义路由信息.GET("/user/{id}", RequestPredicates.accept(MediaType.ALL),userBizHandler ::getUser).GET("/users",userBizHandler ::getUsers).POST("/user",RequestPredicates.accept(MediaType.APPLICATION_JSON),userBizHandler ::getUser).PUT("/user/{id}",RequestPredicates.accept(MediaType.APPLICATION_JSON),userBizHandler ::getUser).DELETE("/user/{id}",userBizHandler ::getUser).build();}
}
@Service
public class UserBizHandler {//查询指定用户idpublic ServerResponse getUser(ServerRequest request) throws Exception{String id = request.pathVariable("id");//获取id//业务处理 Mapper查询数据库People people = new People(1L,"66","123@qq.com",12,"admin");//构造响应return ServerResponse.ok().body(people);}//查询所有用户public ServerResponse getUsers(ServerRequest request) throws Exception {List<People> peoples = Arrays.asList(new People(1L, "66", "123@qq.com", 12, "admin"),new People(2L, "77", "1456@qq.com", 13, "admin"));return ServerResponse.ok().body(peoples);//body中的对象就是@ResponseBody 利用HttpMessageConverter 写出为json}
}

相关文章:

  • Django模型(八)
  • 【Linux】环境基础开发工具的使用(一)
  • Dubbo框架注册中心-Zookeeper搭建
  • 前端自己整理的学习面试笔记
  • 【Vue】2-8、Axios 网络请求
  • Redis核心技术与实战【学习笔记】 - 7.Redis GEO类型 - 面向 LBS 应用的数据类型
  • 深度强化学习(王树森)笔记09
  • Golang的数字签名之旅:crypto/ecdsa库详解
  • 2024美赛数学建模C题思路+模型+代码+论文
  • 提效IntelliJ IDEA插件
  • react经验10:与jquery配合使用
  • 笔记本电脑Win11重装系统教程
  • uniapp H5 px转换rpx
  • K8S网络
  • 与数组相关经典面试题
  • JS 中的深拷贝与浅拷贝
  • 2019年如何成为全栈工程师?
  • 77. Combinations
  • angular2开源库收集
  • Django 博客开发教程 16 - 统计文章阅读量
  • js写一个简单的选项卡
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 后端_MYSQL
  • 解决iview多表头动态更改列元素发生的错误
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 前端相关框架总和
  • 算法-插入排序
  • 想写好前端,先练好内功
  • 小程序开发之路(一)
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 《码出高效》学习笔记与书中错误记录
  • 2017年360最后一道编程题
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​用户画像从0到100的构建思路
  • (4)STL算法之比较
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (超详细)语音信号处理之特征提取
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转载)hibernate缓存
  • ******之网络***——物理***
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .NET BackgroundWorker
  • .NET Core 版本不支持的问题
  • .NET 使用配置文件
  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)
  • .net6 webapi log4net完整配置使用流程
  • .net通用权限框架B/S (三)--MODEL层(2)
  • [ JavaScript ] JSON方法
  • [2008][note]腔内级联拉曼发射的,二极管泵浦多频调Q laser——
  • [BZOJ 1040] 骑士
  • [C#]C#学习笔记-CIL和动态程序集
  • [C#]winform制作仪表盘好用的表盘控件和使用方法
  • [C++]类和对象(中)