SpringBoot3-Web开发

1. 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 接口

2. 静态资源

1. 默认规则

1. 静态资源映射

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

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

2. 静态资源缓存

如前面所述

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

3. 欢迎页

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

  1. 静态资源目录下找 index.html
  2. 没有就在 templates下找index模板页

4. Favicon

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

5. 缓存实验

复制代码
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/**

2. 代码方式

  • 容器中只要有一个 WebMvcConfigurer 组件。配置的底层行为都会生效

  • @EnableWebMvc //禁用boot的默认配置

    //@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));
              }
          };
      }

    }

原因?为什么容器中放一个WebMvcConfigurer就能配置底层行为?

1.WebMvcAutoConfiguration是一个自动配置类,它里面有一个EnableWebMvcConfiguration

  1. EnableWebMvcConfiguration继承于DelegatingWebMvcConfiguration,这两个都生效

3.DelegatingWebMvcConfiguration利用DI把容器中所有的WebConfigurer注入进来

4.别人调用DelegatingWebMvcConfiguration的方法配置底层原则,而他调用所有的WebConfigurer的配置底层方法

3. 路径匹配

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

4. 内容协商

一套系统适配多端数据返回

1. 多端内容适配

1. 默认规则

  1. SpringBoot 多端内容适配
    基于 请求头 内容协商 :(默认开启)
    客户端向服务端发送请求,携带HTTP标准的Accept请求头
    Accept : application/jsontext/xmltext/yaml
    服务端根据客户端请求头期望的数据类型 进行动态返回

    基于 请求参数 内容协商:(需要开启)
    发送请求 GET /projects/spring-boot?format=json
    发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据
    根据参数协商 ,优先返回 json 类型数据【需要开启参数匹配设置
    匹配到 @GetMapping("/projects/spring-boot")

2. 效果演示

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

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

    <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
  2. 标注注解

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

    开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启

    spring.mvc.contentnegotiation.favor-parameter=true

    指定内容协商时使用的参数名。默认是 format

    spring.mvc.contentnegotiation.parameter-name=type

  2. 效果

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. 自定义内容返回

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("[email protected]");
        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. 思考:如何增加其他

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

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

1. @ResponseBodyHttpMessageConverter处理

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

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

上面解释:@ResponseBodyHttpMessageConverter处理

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

    2. 默认MessageConverter有以下

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

    4. jackson用ObjectMapper把对象写出去

2. WebMvcAutoConfiguration提供几种默认HttpMessageConverters

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

默认8个:

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

5. 模板引擎

  • 由于 SpringBoot 使用了嵌入式 Servlet 容器 。所以 JSP 默认是不能使用的。
  • 如果需要服务端页面渲染,优先考虑使用 模板引擎。

模板引擎页面默认放在 src/main/resources/templates

SpringBoot 包含以下模板引擎的自动配置

  • FreeMarker
  • Groovy
  • Thymeleaf
  • Mustache

Thymeleaf官网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" 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. 默认效果
    所有的模板页面在 classpath:/templates/下面找
    找后缀名为.html的页面

2. 基础语法

1. 核心用法

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

  • th:text:标签体内文本值渲染
  • th:utext:不会转义,显示为html原本的样子。

  • th:属性:标签指定属性渲染

  • th:attr:标签任意属性渲染

  • th:if``th:each``...:其他th指令

  • 例如:


    th:attr:任意属性指定

    @{}专门用来取各种路径

表达式**:用来动态取值**

  • {}****:变量取值;使用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. 语法示例

表达式:

  • 变量取值:${...}
  • 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}"

    原内容

    登录

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. 属性优先级

  • 片段

  • 遍历

  • 判断

    • Item description here...

|-------|----------|--------------------------------------|
| Order | Feature | Attributes |
| 1 | 片段包含 | th:insert th:replace |
| 2 | 遍历 | th:each |
| 3 | 判断 | th:if th:unless th:switch th:case |
| 4 | 定义本地变量 | th:object th:with |
| 5 | 通用方式属性修改 | th:attr th:attrprepend th:attrappend |
| 6 | 指定属性修改 | th:value th:href th:src ... |
| 7 | 文本值 | th:text th:utext |
| 8 | 片段指定 | th:fragment |
| 9 | 片段移除 | th:remove |

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:insertth:replace

    © 2011 The Good Thymes Virtual Grocery
    <body>
    </body> <body> 结果: <body>
    © 2011 The Good Thymes Virtual Grocery
    复制代码
      <footer>&copy; 2011 The Good Thymes Virtual Grocery</footer>
    </body>
    </body>

实例:

java 复制代码
<!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/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">

</head>
<body>
<!--导航区 使用公共部分替换-->
<div th:replace="~{common :: myheader}"></div>

<div class="container">
  <a th:href="@{/list}">展示列表1278</a>
</div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>

</body>
</html>

10. devtools 热启动功能

复制代码
      <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。如:
    1. messages.properties:默认
    2. messages_zh_CN.properties:中文环境
    3. messages_en_US.properties:英语环境
  1. 程序中 可以自动注入 MessageSource组件,获取国际化的配置项值

  2. 页面中 可以使用表达式 #{}获取国际化的配置项值

    复制代码
     @Autowired  //国际化取消息用的组件
     MessageSource messageSource;
     @GetMapping("/haha")
     public String haha(HttpServletRequest request){
    
         Locale locale = request.getLocale();
         //利用代码的方式获取国际化配置文件中指定的配置项的值
         String login = messageSource.getMessage("login", null, locale);
         return login;
     }

7. 错误处理

1. 默认机制

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

    1. SpringBoot 会自适应 处理错误响应页面JSON数据
  • 2.SpringMVC的错误处理机制 依然保留,MVC处理不了 ,才会交给boot进行处理

业务发生错误--->@EceptionHandler机制是否能处理--->@ResponseStatus机制能否处理--->SpringMVC定义的默认错误响应是否能处理---都不能时请求转发/error

能处理则响应错误处理结果

  • 发生错误以后,转发给/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. 如果发生了500、404、503、403 这些错误
  2. 如果有模板引擎 ,默认在 classpath:/templates/error/精确码.html
  3. 如果没有模板引擎,在静态资源文件夹下找 精确码.html
  4. 如果匹配不到精确码.html这些精确的错误页,就去找5xx.html4xx.html模糊匹配
  5. 如果有模板引擎,默认在 classpath:/templates/error/5xx.html
  6. 如果没有模板引擎,在静态资源文件夹下找 5xx.html
  7. 如果模板引擎路径templates下有 error.html页面,就直接渲染

2. 自定义错误响应

1. 自定义json响应

使用@ControllerAdvice + @ExceptionHandler 进行统一异常处理

2. 自定义页面响应

根据boot的错误页面规则,自定义页面模板

3. 最佳实战

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

页面,JSON,可用的Model数据如下

8. 嵌入式容器

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

1. 自动配置原理

  • SpringBoot 默认嵌入Tomcat作为Servlet容器。

  • 自动配置类ServletWebServerFactoryAutoConfigurationEmbeddedWebServerFactoryCustomizerAutoConfiguration

  • 自动配置类开始分析功能。`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 导入了 嵌入式的三大服务器 TomcatJettyUndertow

  1. 导入 TomcatJettyUndertow 都有条件注解。系统中有这个类才行(也就是导了包
  2. 默认 Tomcat配置生效。给容器中放 TomcatServletWebServerFactory
  3. 都给容器中 ServletWebServerFactory放了一个web服务器工厂(造web服务器的)
  4. web服务器工厂 都有一个功能, getWebServer获取web服务器
  5. TomcatServletWebServerFactory 创建了 tomcat。
    **4.**ServletWebServerFactory 什么时候会创建 webServer出来。

5.ServletWebServerApplicationContextioc容器,启动的时候会调用创建web服务器

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

7. refresh() 容器刷新 十二大步的刷新子容器会调用 onRefresh()

复制代码
	@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

Web场景的Spring容器启动,在onRefresh的时候,会调用创建web服务器的方法。

Web服务器的创建是通过WebServerFactory搞定的。容器中又会根据导了什么包条件注解,启动相关的 服务器配置,默认EmbeddedTomcat会给容器中放一个 TomcatServletWebServerFactory,导致项目启动,自动创建出Tomcat。

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默认放的服务器工厂,实现自定义嵌入任意服务器

9. 全面接管SpringMVC

  • SpringBoot 默认配置好了 SpringMVC 的所有常用特性。
  • 如果我们需要全面接管SpringMVC的所有配置并禁用默认配置 ,仅需要编写一个WebMvcConfigurer配置类,并标注 @EnableWebMvc 即可

全手动模式

  • @EnableWebMvc : 禁用默认配置
  • WebMvcConfigurer组件:定义MVC的底层行为

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

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

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

2. @EnableWebMvc 禁用默认行为

  1. @EnableWebMvc给容器中导入 DelegatingWebMvcConfiguration组件,

他是 WebMvcConfigurationSupport

  1. WebMvcAutoConfiguration有一个核心的条件注解, @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),容器中没有WebMvcConfigurationSupportWebMvcAutoConfiguration才生效.
  2. @EnableWebMvc 导入 WebMvcConfigurationSupport 导致 WebMvcAutoConfiguration 失效。导致禁用了默认行为
  • @EnableWebMVC 禁用了 Mvc的自动配置
  • WebMvcConfigurer 定义SpringMVC底层组件的功能类

2. WebMvcConfigurer 功能

定义扩展SpringMVC底层功能

|------------------------------------|-----------------------------------------|------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| 提供方法 | 核心参数 | 功能 | 默认 |
| addFormatters | FormatterRegistry | 格式化器:支持属性上@NumberFormat和@DatetimeFormat的数据类型转换 | GenericConversionService |
| getValidator | 无 | 数据校验:校验 Controller 上使用@Valid标注的参数合法性。需要导入starter-validator | 无 |
| addInterceptors | InterceptorRegistry | 拦截器:拦截收到的所有请求 | 无 |
| configureContentNegotiation | ContentNegotiationConfigurer | 内容协商:支持多种数据格式返回。需要配合支持这种类型的HttpMessageConverter | 支持 json |
| configureMessageConverters | List<HttpMessageConverter<?>> | 消息转换器:标注@ResponseBody的返回值会利用MessageConverter直接写出去 | 8 个,支持byte,string,multipart,resource,json |
| addViewControllers | ViewControllerRegistry | 视图映射:直接将请求路径与物理视图映射。用于无 java 业务逻辑的直接视图页渲染 | 无 <mvc:view-controller> |
| configureViewResolvers | ViewResolverRegistry | 视图解析器:逻辑视图转为物理视图 | ViewResolverComposite |
| addResourceHandlers | ResourceHandlerRegistry | 静态资源处理:静态资源路径映射、缓存控制 | ResourceHandlerRegistry |
| configureDefaultServletHandling | DefaultServletHandlerConfigurer | 默认 Servlet:可以覆盖 Tomcat 的DefaultServlet。让DispatcherServlet拦截/ | 无 |
| configurePathMatch | PathMatchConfigurer | 路径匹配:自定义 URL 路径匹配。可以自动为所有路径加上指定前缀,比如 /api | 无 |
| configureAsyncSupport | AsyncSupportConfigurer | 异步支持: | TaskExecutionAutoConfiguration |
| addCorsMappings | CorsRegistry | 跨域: | 无 |
| addArgumentResolvers | List<HandlerMethodArgumentResolver> | 参数解析器: | mvc 默认提供 |
| addReturnValueHandlers | List<HandlerMethodReturnValueHandler> | 返回值解析器: | mvc 默认提供 |
| configureHandlerExceptionResolvers | List<HandlerExceptionResolver> | 异常处理器: | 默认 3 个 ExceptionHandlerExceptionResolver ResponseStatusExceptionResolver DefaultHandlerExceptionResolver |
| getMessageCodesResolver | 无 | 消息码解析器:国际化使用 | 无 |

10. 最佳实践

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

三种方式

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

总结:

给容器中写一个配置类 @Configuration****实现 WebMvcConfigurer****但是不要标注 @EnableWebMvc****注解,实现手自一体的效果。

两种模式

1、前后分离模式@RestController 响应JSON数据

2、前后不分离模式:@Controller + Thymeleaf模板引擎

相关推荐
网安INF30 分钟前
CVE-2024-23897源码分析与漏洞复现(Jenkins 任意文件读取)
java·web安全·网络安全·jenkins·漏洞
oioihoii38 分钟前
C++11列表初始化:从入门到精通
java·开发语言·c++
野生技术架构师44 分钟前
SpringBoot集成Tess4j :低成本解锁OCR 图片识别能力
spring boot·后端·ocr
qqxhb1 小时前
零基础设计模式——行为型模式 - 命令模式
java·设计模式·go·命令模式
sg_knight1 小时前
Ribbon负载均衡实战指南:7种策略选择与生产避坑
java·spring boot·spring·spring cloud·微服务·ribbon·负载均衡
Java初学者小白2 小时前
秋招Day12 - 计算机网络 - 网络安全
java
猩猩之火2 小时前
XWPFTemplate生成word
spring boot·word·动态word
蔡蓝2 小时前
设计模式-适配器模式
java·设计模式·适配器模式
述雾学java2 小时前
Spring Boot + Vue 前后端分离项目解决跨域问题详解
vue.js·spring boot·后端
酷爱码2 小时前
Spring Boot 中实现 HTTPS 加密通信及常见问题排查指南
spring boot·后端·https