SpringBoot3框架,Web开发(下)
模板引擎
- 由于 SpringBoot 使用了嵌入式 Servlet 容器。所以 JSP 默认是不能使用的。
- 如果需要服务端页面渲染,优先考虑使用 模板引擎。
Thymeleaf
Thymeleaf 是一个现代的服务器端 Java 模板引擎,适用于 Web 和独立 环境
格式:
<!DOCTYPE html>
<html xmlns:th="[<http://www.thymeleaf.org>](<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>
Thymeleaf整合
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
自动配置原理
- 开启了 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration 自动配置
- 属性绑定在 ThymeleafProperties 中,对应配置文件 spring.thymeleaf 内容
- 所有的模板页面默认在
classpath:/templates
文件夹下- 默认效果
- 所有的模板页面在
classpath:/templates/
下面找- 找后缀名为
.html
的页面
Thymeleaf基础语法
核心用法
th:xxx
:动态渲染指定的 html 标签属性值、或者th指令(遍历、判断等)
th:text
:标签体内文本值渲染
th:utext
:不会转义,显示为html原本的样子。即会识别html语法th:属性
:标签指定属性渲染th:attr
:标签任意属性渲染,可以指定多个属性,语法为:例如,要指定src和style属性,th:attr=”src=${imgUrl},style=${style}”th:if th:each...
:其他th指令
th:if
:可以传入布尔类型的值,true表示该标签生效,false表示该标签不生效
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>
th:each
:遍历操作,语法:th:each=”元素名 : ${集合}”或者th:each=”元素名,迭代状态 : ${集合}”,迭代状态有以下取值:
- index:当前遍历元素的索引,从0开始
- count:当前遍历元素的索引,从1开始
- size:需要遍历元素的总数量
- current:当前正在遍历的元素对象
- even/odd:是否偶数/奇数行
- first:是否第一个元素
- last:是否最后一个元素
<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>
使用状态信息直接在表中加一列 stats.迭代状态 (也是使用${stats.迭代状态})就可以在表中显示出每一行(集合中的每个元素)的状态信息
表达式
${}
:变量取值,使用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生成工具
这些工具类中有一系列函数可以调用,见官网文档
一些其他操作
文本操作:
- 字符串拼接: +
- 文本替换:| 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)
行内写法:
使用
[[...]] or [(...)]
在行内使用其要引入的数据
<p>Hello, [[${session.user.name}]]!</p>
属性优先级
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 |
变量选择
使用th:object属性指定一个对象,再使用*{}获取其对象的属性,例:
<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>
Thymeleaf模板布局
- 定义模板:
th:fragment
- 引用模板:
~{templatename(模板名)::selector(片段名)}
- 插入模板:
th:insert
、th:replace
<footer th:fragment="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> © 2011 The Good Thymes Virtual Grocery</footer>
</div>
<footer> © 2011 The Good Thymes Virtual Grocery</footer>
</body>
</body>
devtools
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
修改页面后;ctrl+F9
刷新效果;
java代码的修改,如果devtools
热启动了,可能会引起一些bug,难以排查
国际化
实现步骤:
- Spring Boot 在类路径根下查找messages资源绑定文件。文件名为:messages.properties
- 多语言可以定义多个消息文件,命名为
messages_区域代码.properties
。如:
messages.properties
:默认messages_zh_CN.properties
:中文环境messages_en_US.properties
:英语环境- 在程序中可以自动注入
MessageSource
组件,获取国际化的配置项值- 在页面中可以使用表达式
#{}
获取国际化的配置项值
@Autowired //国际化取消息用的组件
MessageSource messageSource;
@GetMapping("/haha")
public String haha(HttpServletRequest request){
Locale locale = request.getLocale();
//利用代码的方式获取国际化配置文件中指定的配置项的值
String login = messageSource.getMessage("login", null, locale);
return login;
}
错误处理
错误处理的自动配置都在ErrorMvcAutoConfiguration
中,两大核心机制:
- SpringBoot 会自适应处理错误,响应页面或JSON数据
- SpringMVC的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理
- 发生错误以后,到了SpringBoot,转发给/error路径,SpringBoot在底层写好一个 BasicErrorController的组件,专门处理这个请求
- 再通过与客户端的内容协商,选择返回json数据还是选择返回具体页面
- 如果是返回json则将信息转化为json形式返回
- 如果是返回页面,会找解析错误的自定义视图地址,如果找不到,默认的错误也就是error页
- 如果模板引擎路径
templates
下有error.html
页面,就直接渲染- 容器中有一个错误视图解析器
- 如果发生了500、404、503、403 这些错误
- 如果有模板引擎,默认在
classpath:/templates/error/**精确码.html**
- 如果没有模板引擎,在静态资源文件夹下找
精确码.html
- 如果匹配不到
精确码.html
这些精确的错误页,就去找5xx.html
,4xx.html
模糊匹配
- 如果有模板引擎,默认在
classpath:/templates/error/5xx.html
- 如果没有模板引擎,在静态资源文件夹下找
5xx.html
容器中有一个默认的名为 error 的 view; 提供了默认白页功能
建议错误处理:
- 前后分离
- 后台发生的所有错误,
@ControllerAdvice + @ExceptionHandler
进行统一异常处理。- 服务端页面渲染
- 不可预知的一些,HTTP码表示的服务器或客户端错误
- 给
classpath:/templates/error/
下面,放常用精确的错误码页面。500.html
,404.html
- 给
classpath:/templates/error/
下面,放通用模糊匹配的错误码页面。5xx.html
,4xx.html
- 发生业务错误
- 核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页。
- 通用业务,
classpath:/templates/error.html
页面,显示错误信息。
错误信息页面可以使用一些model数据用行内写法[[ ]]和变量取值${}(对这些错误的信息的model数据变量取值)加在页面上
可用的model数据:
- timestamp:错误的时间信息
- status:状态码
- error:错误类型
- trace:错误堆信息
- message:错误信息
- path:错误对应的路径
嵌入式容器
Servlet容器:管理、运行Servlet组件(Servlet、Filter、Listener)的环境,一般指服务器
自动配置原理
- SpringBoot 默认嵌入Tomcat作为Servlet容器。
- 自动配置类是
ServletWebServerFactoryAutoConfiguration
,EmbeddedWebServerFactoryCustomizerAutoConfiguration
- 自动配置类开始分析功能。
xxxxAutoConfiguration
ServletWebServerFactoryAutoConfiguration
自动配置了嵌入式容器场景- 绑定了
ServerProperties
配置类,所有和服务器有关的配置server
ServletWebServerFactoryAutoConfiguration
导入了 嵌入式的三大服务器Tomcat
、Jetty
、Undertow
- 导入
Tomcat
、Jetty
、Undertow
都有条件注解。系统中有这个类才行(也就是导了包)- 都给容器中
ServletWebServerFactory
放了一个 web服务器工厂(造web服务器的)- web服务器工厂 都有一个功能,
getWebServer
获取web服务器- TomcatServletWebServerFactory 创建了 tomcat。
ServletWebServerApplicationContext
ioc容器,启动的时候会调用创建web服务器- Spring**容器刷新(启动)**的时候,会预留一个时机,用来刷新子容器。
onRefresh()
,刷新子容器时会调用onRefresh()
总结:
- Web场景的Spring容器启动,在onRefresh的时候,会调用创建web服务器的方法。
- Web服务器的创建是通过WebServerFactory搞定的。容器中又会根据导了什么包条件注解,启动相关的 服务器配置,默认
EmbeddedTomcat
会给容器中放一个TomcatServletWebServerFactory
,导致项目启动,自动创建出Tomcat- 修改server下的相关配置就可以修改服务器参数
全面接管SpringMVC(全手动)
全面接管SpringMVC即使用全手动模式配置SpringMVC,实现WebMvcConfiger接口,再在配置类上加上@EnableWebMvc注解,再手动配置SpringMvc
WebMvcAutoConfiguration
WebMvcAutoConfiguration
是web场景的自动配置类,会自动导入一些配置:
- 支持RESTful的filter:HiddenHttpMethodFilter
- 支持非POST请求,请求体携带数据:FormContentFilter
- 导入
EnableWebMvcConfiguration
:导入了HandlerAdapter,HandlerMapping,一些解析器等组件WebMvcAutoConfigurationAdapter
配置生效,会导入一些视图解析器,内容协商解析器,过滤器,静态资源链等相关的mvc底层组件
@EnableWebMvc
@EnableWebMvc
给容器中导入DelegatingWebMvcConfiguration
组件,他是WebMvcConfigurationSupport
WebMvcAutoConfiguration
有一个核心的条件注解,@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
,容器中没有WebMvcConfigurationSupport
,WebMvcAutoConfiguration
才生效.- @EnableWebMvc 导入
WebMvcConfigurationSupport
导致WebMvcAutoConfiguration
失效。导致禁用了默认行为
但是建议使用手自一体方式
Web新特性
ProblemDetails
- ProblemDetailsExceptionHandler是一个@ControllerAdivce,集中处理系统的异常
- ProblemDetailsExceptionHandler只能处理springmvc的一些指定异常
- ProblemDetails默认是关闭的
开启ProblemDetails返回, 使用新的MediaType
Content-Type: application/problem+json
+ 额外扩展返回,例:
{
"type": "about:blank",
"title": "Method Not Allowed",
"status": 405,
"detail": "Method 'POST' is not supported.",
"instance": "/list"
}
函数式Web
步骤:
- 给容器中放一个Bean:RouterFunction<ServerResponse>
- 核心四大对象:
- RouterFunction:定义路由信息(请求内容,接收请求的handler)
- RequestPredicate:定义请求(请求方式,请求参数)
- ServerRequest:封装请求完整信息
- ServerResponse:封装响应完整信息
- 核心四大对象:
- 先定义路由信息,再定义请求,最后封装完整请求和响应的信息,例:
@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();
}
}
@Component
public class MyUserHandler {
public ServerResponse getUser(ServerRequest request) {
...//具体业务
return ServerResponse.ok().build();
}
public ServerResponse getUserCustomers(ServerRequest request) {
...//要获取请求的信息可以使用request对象的一系列方法
return ServerResponse.ok().body(...);//在body中的对象,会以json形式返回
}
public ServerResponse deleteUser(ServerRequest request) {
...
return ServerResponse.ok().build();
}
}