SpringBoot源码解析-02

5. 模板引擎

  • 由于 SpringBoot 使用了嵌入式 Servlet 容器 (tomca)。所以 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>

5.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. 默认效果

    1. 所有的模板页面在 classpath:/templates/下面找
    2. 找后缀名为.html的页面
使用举例:
1.html文件:

在这里插入图片描述

2.controller:
注意,这里只能写controller,thymeleaf是类似于前后端不分离模式:

在这里插入图片描述

5.2. 基础语法:

在html文件中html标签中加上下列代码就有thymeleaf代码提示。
<html xmlns:th="http://www.thymeleaf.org">
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.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'))

5.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}" />

5.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:是否最后一个元素
注意,对于下面的代码:
<td th:text="${prod.name}">Onions</td>
我们也可以写成:
<td> [[${prod.name}]] </td>

5.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>

在这里插入图片描述

5.6. 属性优先级

●片段

●遍历

●判断

<ul>
  <li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>
</ul>
OrderFeatureAttributes
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

5.7. 行内写法

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

5.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

5.9. 模板布局

  • 定义模板: th:fragment
  • 引用模板:~{templatename::selector}
  • 插入模板:th:insertth: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>
在运用中,我们一般把公共页面要运用的东西单独写在一个类里面,并制定他的名字(比如这段代码的首行),这样,在其他html文件需要用到这些公共的部分,比如导航栏,就可以直接引入模板:
 <div th:insert="~{footer :: copy}"></div>
copy是名字,footer是thymeleaf路径下的footer.html文件。

5.10. devtools

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
      </dependency>
动态改变,热编译。
修改页面后;ctrl+F9刷新效果;
注意,java代码的修改,如果devtools热启动了,可能会引起一些bug,难以排查。
5.11.其他配置:

在这里插入图片描述

cache是缓存相关的,开发时为了不清缓存就看到效果,记得关闭,当然,上线时记得开启。

6. 国际化

国际化的自动配置参照MessageSourceAutoConfiguration

注意,一定要确保编码为UTF-8。

实现步骤

  1. Spring Boot 在类路径根下查找messages资源绑定文件。文件名为:messages.properties

  2. 多语言可以定义多个消息文件,命名为messages_区域代码.properties。如:

    1. messages.properties:默认
    2. messages_zh_CN.properties:中文环境
    3. messages_en_US.properties:英语环境
  3. 程序中可以自动注入 MessageSource组件,获取国际化的配置项值

  4. 页面中可以使用表达式 #{}获取国际化的配置项值,{}填写配置文件中的key。即下面的login。

login=Login
login=登录
<h1 th:text="#{login}"></h1> 
国际化配置:

在这里插入图片描述

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

7. 错误处理

7.1.默认机制

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

  1. SpringBoot 会自适应处理错误,响应页面或JSON数据
  2. SpringMVC的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理
    在这里插入图片描述
以前常用的自定义全局错误处理:

在这里插入图片描述

注意,标明@ControllerAdvice就是全局处理,如果写在一个类的内部,即只是处理该类的错误。
如果还是不能处理错误,那么就转发给/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;
	}
解析如下:
resolveErrorView 方法

这个方法尝试解析一个与HTTP状态码对应的错误视图。

  1. 参数
    • HttpServletRequest request:当前的HTTP请求。
    • HttpStatus status:HTTP状态码。
    • Map<String, Object> model:模型数据,用于渲染视图。
  2. 方法内容
    • 首先,它尝试解析一个与具体状态码值对应的视图(例如404500等)。
    • 如果没有找到与具体状态码值对应的视图,它会检查状态码所属的系列(如4xx5xx),并尝试解析一个与系列对应的通用视图。
    • 如果两者都失败,则返回null
resolve 方法

这个方法尝试解析一个特定的视图名称。

  1. 参数
    • viewName:视图名称。
    • model:模型数据。
  2. 方法内容
    • 构造一个具体的视图名称(如error/404)。
    • 使用TemplateAvailabilityProvider检查该视图是否可用。
    • 如果可用,返回一个新的ModelAndView对象。
    • 如果不可用,调用resolveResource方法尝试从资源位置解析视图。
resolveResource 方法

这个方法尝试从静态资源位置解析一个视图。

  1. 参数

    • viewName:视图名称。
    • model:模型数据。
  2. 方法内容

    • 遍历所有静态资源位置。
    • 对于每个位置,尝试构造一个对应的资源路径(如static/error/404.html)。
    • 如果资源存在,返回一个新的ModelAndView对象,该对象使用HtmlResourceView来渲染HTML资源。
    • 如果在遍历过程中发生异常,则捕获异常并继续尝试下一个位置。
    • 如果所有位置都没有找到对应的资源,返回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();
	}
规则:
解析一个错误页:

在这里插入图片描述

7.2. 自定义错误响应

1. 自定义json响应

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

2. 自定义页面响应

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

7.3. 最佳实战

在这里插入图片描述

页面,JSON,可用的Model数据如下image.png
在响应的页面中,可以用插值表达式返回错误的trace。

8. 嵌入式容器

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 {

}

在这里插入图片描述

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

用法:

  • 修改server下的相关配置就可以修改服务器参数
  • 通过给容器中放一个ServletWebServerFactory,来禁用掉SpringBoot默认放的服务器工厂,实现自定义嵌入任意服务器
切换服务器:
<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>

9. 全面接管SpringMVC

  • SpringBoot 默认配置好了 SpringMVC 的所有常用特性。

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

  • 全手动模式

    • @EnableWebMvc : 禁用默认配置
    • WebMvcConfigurer组件:定义MVC的底层行为
9.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:内容协商管理器
  2. 4.WebMvcAutoConfigurationAdapter配置生效,它是一个WebMvcConfigurer,定义mvc底层组件
      1. 定义好 WebMvcConfigurer 底层组件默认功能;所有功能详见列表
      2. 视图解析器:InternalResourceViewResolver
      3. 视图解析器:BeanNameViewResolver,**视图名(controller方法的返回值字符串)**就是组件名
      4. 内容协商解析器:ContentNegotiatingViewResolver
      5. 请求上下文过滤器:RequestContextFilter: 任意位置直接获取当前请求
      6. 静态资源链规则
      7. ProblemDetailsExceptionHandler:错误详情
        1. SpringMVC内部场景异常被它捕获:
  3. 5.定义了MVC默认的底层行为: WebMvcConfigurer

9.2. WebMvcConfigurer 功能

定义扩展SpringMVC底层功能

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

9.3. @EnableWebMvc 禁用默认行为

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

​ 他是 WebMvcConfigurationSupport

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

在这里插入图片描述

两种模式

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

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

11. Web新特性

11.1. Problemdetails

RFC 7807: https://www.rfc-editor.org/rfc/rfc7807

错误信息返回新格式

注意,不是默认开启。
spring.mvc.problemdetails.enabled=true
可以加上这句话。
@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",
    "message": "Method 'POST' is not supported.",
    "path": "/list"
}

开启ProblemDetails返回, 使用新的MediaType

image-20240417131852967

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

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

11.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. 核心类

在这里插入图片描述

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();
    }

}
配置路由的格式举例:
GET("/{user}", ACCEPT_JSON, userHandler::getUser)
  1. "/{user}" - 路径模板

    这个参数定义了请求的URL路径模式。在这里,/{user} 是一个路径模板,其中 {user} 是一个占位符。当实际的HTTP请求到达时,服务器会尝试将请求的URL与这个模板进行匹配。如果匹配成功,{user} 将被替换为请求URL中对应部分的实际值。例如,如果请求的URL是 /john,那么 {user} 的值就是 john

    路径模板允许你定义具有可变部分的URL,这使得你的应用程序能够处理多个不同的用户请求,而不需要为每个用户都定义一个单独的路由。

  2. ACCEPT_JSON - 请求条件

    这个参数通常用于指定处理该路由时所需的请求条件。在这里,ACCEPT_JSON 可能是一个常量或配置,用于指示该路由只处理那些请求头中包含 Accept: application/json 的请求。这意味着客户端期望服务器返回的响应体是JSON格式的。

    通过指定请求条件,你可以确保你的应用程序只处理那些符合特定要求的请求,这有助于保持代码的清晰性和可维护性。

  3. userHandler::getUser - 处理函数

    这个参数是一个函数指针或可调用对象,它定义了当请求的URL与路径模板匹配并且满足请求条件时应该调用的函数或方法。在这个例子中,userHandler::getUser 是一个成员函数指针,它指向 userHandler 类中定义的 getUser 方法。

    当路由被触发时,getUser 方法会被调用,并且通常会传入一些参数,比如请求对象、响应对象以及从URL路径中提取的任何动态部分(如 {user} 的值)。然后,getUser 方法会执行相应的逻辑来处理请求,并可能生成一个响应返回给客户端。

    我们一般在其他的类中集中写出处理函数,然后将该类传参传入。举例:
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();
    }

}
写法举例:

在这里插入图片描述

如果有数据,可以放在build中返回。
注意,可以通过request获取数据。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java NIO,高效操作I/O流的必备技能

Java IO在工作中其实不常用到&#xff0c;更别提NIO了。但NIO却是高效操作I/O流的必备技能&#xff0c;如顶级开源项目Kafka、Netty、RocketMQ等都采用了NIO技术&#xff0c;NIO也是大多数面试官必考的体系知识。虽然骨头有点难啃&#xff0c;但还是要慢慢消耗知识、学以致用哈…

设计模式:观察者模式(Observer)

设计模式&#xff1a;观察者模式&#xff08;Observer&#xff09; 设计模式&#xff1a;观察者模式&#xff08;Observer&#xff09;模式动机模式定义模式结构时序图模式实现观察者模式在单线程环境下的测试观察者模式在多线程环境下的测试多线程下的观察者模式模式分析优缺点…

计算机不联网是否有IP地址

计算机不联网是否会有IP地址&#xff0c;这个问题涉及到计算机网络的基础知识。要深入探讨这个问题&#xff0c;我们需要从IP地址的定义、作用&#xff0c;以及计算机在不联网状态下的网络配置等多个方面进行分析。 首先&#xff0c;IP地址&#xff08;Internet Protocol Addre…

HCIA--综合实验(超详细)

要求&#xff1a; 1. 使用172.16.0.0/16划分网络 2.使用ospf协议合理规划区域保证更新安全 3.加快收敛速度 4. r1为DR没有BDR 5.PC2&#xff0c;3&#xff0c;4&#xff0c;5自动获取IP地址&#xff1b;PC1为外网&#xff0c;PC要求可用互相访问 6.r7为运营商&#xff0c;只能配…

Oracle 正则,开窗,行列转换

1.开窗函数 基本上在查询结果上添加窗口列 1.1 聚合函数开窗 基本格式: ..... 函数() over([partition by 分组列,...][order by 排序列 desc|asc][定位框架]) 1&#xff0c;partition by 字段 相当于group by 字段 起到分组作用2&#xff0c;order by 字段 即根据某个字段…

Java实现优先级队列(堆)

前言 在学习完二叉树的相关知识后&#xff0c;我们对数据结构有了更多的认识&#xff0c;本文将介绍到优先级队列(堆&#xff09; 1.优先级队列 1.1概念 前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能…

万兆以太网MAC设计(1)10G PCS PMA IP核使用

文章目录 一、设计框图二、模块设计三、IP核配置四、上板验证五、总结 一、设计框图 关于GT高速接口的设计一贯作风&#xff0c;万兆以太网同样如此&#xff0c;只不过这里将复位逻辑和时钟逻辑放到了同一个文件ten_gig_eth_pcs_pma_0_shared_clock_and_reset当中。如果是从第…

将图片按灰度转换成字符

from struct import *ch [., :, !, ~, ,, ^, *,$, #] ch.reverse()def calc(R, G, B):#模式Lreturn R * 299 // 1000 G * 587 // 1000 B * 144 / 1000def character( val):num val / 260 * len(ch)num round(num)if num>len(ch):numlen(ch)-1return ch[num]class rmb:d…

浅谈Spring的Bean生命周期

在Spring框架中&#xff0c;Bean&#xff08;即Java对象&#xff09;的生命周期涵盖了从创建到销毁的全过程&#xff0c;主要包含以下几个阶段&#xff1a; 实例化&#xff08;Instantiation&#xff09;&#xff1a; 当Spring IoC容器需要创建一个Bean时&#xff0c;首先会通过…

【安全设备】Hfish如何测试

部署好了蜜罐如何测试&#xff1f; 1、查找节点 2、节点的端口 3、将部署的主机和节点联系在一起 http访问。 就可以测试了。

Win10系统VScode远程连接VirtualBox安装的Ubuntu20.04.5

1.打开虚拟机&#xff0c;在中端中输入命令: sudo apt-get install openssh-server 安装ssh 我这里已经安装完成&#xff0c;故显示是这样 2.输入命令&#xff1a;sudo systemctl start ssh 启动远程连接 注意&#xff0c;如果使用VirtualBox安装的虚拟机&#xff0c;需要启用…

Git分布式版本控制系统——在IDEA中使用Git(一)

一、在IDEA中配置Git 本质上还是使用的本地安装的Git软件&#xff0c;所以需要在IDEA中配置Git 打开IDEA的设置页面&#xff0c;按照下图操作 二、在IDEA中使用Git获取仓库 1、本地初始化仓库 2、从远程仓库克隆 方法一&#xff1a; 方法二&#xff1a; 三、.gitignore文件…

测绘管理与法律法规 | 测绘资质分类分级标准 | 学习笔记

目录 1. 申请条件 2.审批程序 3.专业技术人员的特殊规定 1. 申请条件 法人资格&#xff1a;申请单位必须具有法人资格。 专业技术人员&#xff1a;需拥有与测绘活动相适应的测绘专业技术人员和相关专业技术人员。 技术装备&#xff1a;具备与测绘活动相适应的技术装备和设…

js-利用blur使文本框自动控制格式

在 JavaScript 中&#xff0c;blur 是一个事件&#xff0c;它在一个元素失去焦点时触发。当用户从一个元素中移开或者将焦点转移到页面上的另一个元素时&#xff0c;该元素将触发 blur 事件。这个事件通常用于验证用户输入或执行其他与用户交互相关的操作。 假设我有个文本框&…

工业物联网让“制造”变成“智造”!——青创智通

工业物联网解决方案-工业IOT-青创智通 随着科技的不断进步和工业的持续发展&#xff0c;物联网&#xff08;IoT&#xff09;技术的出现为制造业带来了前所未有的变革。工业物联网&#xff08;IIoT&#xff09;作为物联网技术在工业领域的应用&#xff0c;正在逐渐改变传统的制…

Netty学习——高级篇2 Netty解码技术

接上篇&#xff1a;Netty学习——高级篇1 拆包 、粘包与编解码技术&#xff0c;本章继续介绍Netty的其他解码器 1 DelimiterBasedFrameDecoder分隔符解码器 DelimiterBasedFrameDecoder 分隔符解码器是按照指定分隔符进行解码的解码器&#xff0c;通过分隔符可以将二进制流拆分…

数据的插入、修改和删除

一、 插入数据 1. 向表中所有字段插入数据 &#xff08;1&#xff09; 指定所有字段及其相对应的值 insert into 表名(字段1&#xff0c;字段2&#xff0c;……) values(字段值1&#xff0c;字段值2&#xff0c;……);**【案例】**向goods表中插入一条新记录 步骤1&#xff…

密码学 | 椭圆曲线数字签名方法 ECDSA(上)

目录 1 ECDSA 是什么&#xff1f; 2 理解基础知识 3 为什么使用 ECDSA&#xff1f; 4 基础数学和二进制 5 哈希 6 ECDSA 方程 7 点加法 8 点乘法 9 陷阱门函数&#xff01; ⚠️ 原文&#xff1a;Understanding How ECDSA Protects Your Data. ⚠️ 写在前面…

Java+saas模式 智慧校园系统源码Java Android +MySQL+ IDEA 多校运营数字化校园云平台源码

Javasaas模式 智慧校园系统源码Java Android MySQL IDEA 多校运营数字化校园云平台源码 智慧校园即智慧化的校园&#xff0c;也指按智慧化标准进行的校园建设&#xff0c;按标准《智慧校园总体框架》中对智慧校园的标准定义是&#xff1a;物理空间和信息空间的有机衔接&#…

第七周学习笔记DAY.1-封装

学完本次课程后&#xff0c;你能够&#xff1a; 理解封装的作用 会使用封装 会使用Java中的包组织类 掌握访问修饰符&#xff0c;理解访问权限 没有封装的话属性访问随意&#xff0c;赋值也可能不合理&#xff0c;为了解决这些代码设计缺陷&#xff0c;可以使用封装。 面向…
最新文章