1. 静态资源
默认情况下,SpringBoot 支持映射获取当前项目下的任何静态资源,其中静态资源需要放在特定的静态资源文件夹下,如所示:
1 | "classpath:/META-INF/resources/", |
这些静态资源文件夹的定义在 org.springframework.boot.autoconfigure.web.ResourceProperties
中便有体现,具体如下所示:
1 | ( |
如果我们要访问 jar 中的静态资源(webjars 中的资源),那么我们需要去对应 jar 中的 classpath:/META-INF/resources/webjars/
路径下找资源。我们以访问 jquery/3.3.1/jquery.js
为例。
首先我们需要导入 JQuery 的依赖,具体如下所示:
1 | <dependency> |
在访问 jar 包中的资源的时候我们只需要写 webjars 下面资源的名称即可,例如访问路径为 localhost:8080/webjars/jquery/3.3.1/jquery.js
,浏览器上便可以显示 jquery.js 的数据。
2. 欢迎页面
SpringBoot 支持自动识别静态资源文件夹
下的欢迎页面 index.html
。当我们请求路径为 localhost:8080/
的时候,在存在 index.html 的情况下会显示该页面。
3. 网站图标
与其他静态资源一样,Spring Boot 在已配置的静态文件夹中查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的图标。
4. 模板引擎
Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 JSP,Velocity,FreeMaker 等,它也可以轻易的与 Spring MVC 等Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用(按 ctrl + F9 重新编译页面)。
Spring Boot 推荐使用 Thymeleaf、Freemarker 等后现代的模板引擎技术;一但导入相关依赖,会自动配置ThymeleafAutoConfiguration、FreeMarkerAutoConfiguration 等自动配置类。
下图描述了模板引擎的工作原理,模板引擎通过将静态页面和 Model 中的数据结合渲染输出我们需要的页面。如下图所示:
下面我们介绍一个 SpringBoot 中如何使用 Thymeleaf 吧。
4.1 引入 Thymeleaf
1 | <!--导入模板引擎--> |
4.2 使用 Thymeleaf
我们查看自动配置包中关于模板引擎 Thymeleaf 的自动配置绑定文件类,以下是相关的内容:
1 | ( |
从代码中我们也可以知道 Thymeleaf 模板是存放在 templates 文件夹中的 html 文件,这样 Thymeleaf 就能自动渲染页面。
下面我们来简单的演示一下 Thymeleaf 的使用,首先我们编写如下控制器类 IndexController,具体内容如下:
1 |
|
接着在编写模板文件 home.html,具体内容如下:
1 |
|
需要注意的是,我们需要导入 Thymeleaf 的名称空间,即<html lang="en" xmlns:th="http://www.thymeleaf.org">
。关于该模板引擎的具体使用如语法规则之类的我们可以查看其官方文档。
5. 定制web扩展配置
5.1 定制 SpringMVC 配置
SpringBoot 自动配置好了 SpringMVC。以下是 SpringBoot 对 SpringMVC 的一些默认配置,除此之外,我们还介绍了如何自定义 SpringMVC 的一些配置。关于 SpringBoot 对 SpringMVC 自动配置的详细信息,我们可以在自动配置包中的 WebMvcAutoConfiguration 类中找到其中的源码定义。
5.1.1 定制视图解析器
SpringBoot 自动配置了多种视图解析器,包括组合所有的视图解析器的 ContentNegotiatingViewResolver
以及解析各种 Bean 的BeanNameViewResolver
。
我们可以自己给容器中添加一个视图解析器,SpringBoot 会自动的将其组合进来。自定义视图解析器的方法很简单。首先我们需要编写自己的视图解析器类,该类需要实现 ViewResolver
接口,然后重写 resolveViewName 方法。具体如下所示:
1 | /** |
接着在配置类中编写一个方法,并将该方法返回的自定义视图解析器对象 MyViewResolver 加入到 IoC 容器中即可。至此,自定义视图解析的添加边完成了。
1 | /** |
我们可以在 DispatchServlet 类的 都 doDispatch 方法上打上断点进行测试,其中我们自定义的视图解析器是有起作用的,如下图所示:
5.1.2 定制转换器和格式化器
SpringBoot 自动注册了 Converter
,GenericConverter
,Formatter
等组件,其中 Converter
即为转换器,比如请求时可以将表单内容自动封装转换为一个类;Formatter
即为格式化器,如对时间的进行格式化等。与自定义视图解析器一样,我们也可以通过同样的方式来自定义转换器和格式化器等。
另外,SpringBoot 还支持 HttpMessageConverters
,其是 SpringMVC 用来转换 Http 请求和响应的,比如将 User 转换为 Json 格式。假如我们需要自己给容器中添加 HttpMessageConverter
,只需要类似前面自定义视图解析器一样的方法将自己的组件注册容器中即可。示例如下:
1 | false) (proxyBeanMethods = |
总结:前面 5.1.1 和 5.1.2 使用的方法都是一样的,都是在配置类中将自定义的类作为组件加入到容器中,这样 SpringBoot 在自动配置很多组件的时候,会先看容器中有没有用户自己配置的(@Bean、@Component)组件。如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)则会将用户配置的和自己默认的组合起来。
5.1.3 定制拦截器
与前面使用普通的配置类配置自定义组件不同的是,下面我们使用 WebMvcConfigurer
类型的配置类来实现添加自定义的拦截器。使用这种方法,可以避免用户自定义组件替代 SpringBoot 自动配置组件的情况。相反的,用户自定义配置的组件会和 SpringBoot 自动配置的组件一起其作用。下面我们就以定义拦截器为例演示一下该功能的使用。
首先我们自然需要先编写一个自定义的拦截器类,其需要实现 HandlerInteceptor 接口,并实现需要的方法,如下所示:
1 | public class IndexInterceptor implements HandlerInterceptor { |
接着我们需要编写 WebMvcConfigurer
类型的配置类来注册该拦截器,具体如下所示:
1 |
|
运行之后,我们可以在控制台上发现拦截器的 preHandle 方法运行了。。。
的信息被输出了,说明我们拦截器添加成功了。
原理:
我们查看 MVC 的自动配置类 WebMvcAutoConfiguration ,该类中的静态内部类 EnableWebMvcConfiguration 的父类 DelegatingWebMvcConfiguration 存在如下代码:
1 | ( |
我们观察 setConfigurers 方法,该方法会从容器中获取所有的 WebMvcConfigurer(含有我们自定义的 WebMvcConfig),并添加到 WebMvcConfigurerComposite 中。最后,我们配置类 WebMvcConfig 中拦截器也会一并被调用执行。
5.2 全面接管 SpringMVC
假如我们不需要 SpringBoot 对 SpringMVC 的自动配置,也就是所有的 SpringMVC 的配置都要我们自己手动配置,我们只需要在配置类中添加 @EnableWebMvc 即可。
原理:
为什么 @EnableWebMvc 会使 SpringMVC 的自动配置实效,我们来查看一下其源码,具体如下所示:
1 | (RetentionPolicy.RUNTIME) |
我们发现 @EnableWebMvc 本身需要导入 DelegatingWebMvcConfiguration 该类,而 DelegatingWebMvcConfiguration 是 WebMvcConfigurationSupport 的子类。如下所示:
1 | public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { |
另外,MVC 自动配置类中定义了 @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
。它表明Spring 容器中缺少 WebMvcConfigurationSupport 类时,该自动配置类才会生效。由于前面我们在自定义的配置类中使用了 @EnableWebMvc 注解,该注解会导入 WebMvcConfigurationSupport 该类,所以我们才能全面接管 SpringMVC 的配置。
6. 国际化
步骤:
1)、编写国际化配置文件,抽取页面需要显示的国际化消息
2)、SpringBoot 自动配置好了管理国际化资源文件的组件 ResourceBundleMessageSource,所以这一步我们不需要自己配置。
1 | "spring.messages") (prefix = |
3)、页面获取国际化的值,具体代码如下:
1 |
|
4)、在 SpringBoot 的配置文件中配置国际化的命名空间,具体如下所示:
1 | # 配置命名空间,用于指明国际化文件的位置 |
最后的效果就是会根据浏览器语言设置的信息切换了国际化。
7. 错误处理机制
7.1 默认错误处理机制
当我们向 SpringBoot 应用发送一个不存在的请求地址的时候,在浏览器中则会显示服务端响应返回一个默认的错误页面,如下所示:
不过当我们使用其他客户端向 SpringBoot 应用发送一个不存在的请求地址的时候,比如使用 Postman 发送。此时则会显示服务端发送回来的 Json 数据,例如:
1 | { |
我们从各自的发送请求中得到这种差异的原因,比如浏览器的请求头表明其想要接收的数据是 text/html
的数据,如下:
相反地,我们使用 Postman 发送的请求的请求头则不是要求接收 text/html
类型的数据,具体如下所示:
当然这种差异的具体原因我们可以在源码中找到,我们可以参照 ErrorMvcAutoConfiguration 自动配置类的具体内容,该类主要是用于错误处理的自动配置类。这里不再详述。
7.2 自定义错误响应
7.2.1 定制错误页面
1)、在有配置模板引擎的情况下,如果发生指定状态类型的错误时,Springboot 会将模板文件夹里面的 error 文件夹下命名为对应状态码的 错误状态码.html
的文件作为自定义的错误页面。比如请求时发生404错误,那么就会将对应的名为404.html
文件作为自定义错误页面。
另外,我们也可以使用名为4xx.html
或5xx.html
作为自定义错误页面来匹配这种类型的所有错误,但是如果发生指定状态类型的错误时,会优先寻找名为对应状态码的自定义错误页面。
2)、在没有配置模板引擎或者模板文件夹下没有需要的错误页面的情况下,那么就会在静态资源文件夹下找。
3)、以上的情况下都没有找到需要的错误页面是,就会默认来到 SpringBoot 默认的错误提示页面。
需要说明的是,我们在页面能获取的信息有:
- timestamp:时间戳
- status:状态码
- errors:错误提示
- exception:异常对象
- message:异常消息
- errors:JSR303数据校验的错误都在这里
假如我在 Controller 的 index 方法中故意导致服务器除零异常,具体代码如下所示:
1 |
|
使用模板引擎 Thymeleaf 的页面编写示例如下:
1 |
|
运行测试之后,浏览器页面上的异常信息显示具体如下图所示:
7.2.2 自定义错误响应数据
当出现错误的时候,框架底层会进行 /error 请求,然后被 BasicErrorController 处理,其中异常响应出去的数据是由 getErrorAttributes 得到的。该方法是 AbstractErrorController 或 ErrorController 规定的方法:
完全自己编写一个 ErrorController 的实现类(或编写 AbstractErrorController 的子类),并加入到 IoC 容器中;
页面上显示的异常响应数据,或者是 Json 返回的异常响应数据都是通过 ErrorAttributes.getErrorAttributes 方法得到的。
下面,我们自定义 ErrorAttributes 类,具体如下所示:
1 |
|
同样的,当 Controller 的 index 方法发生除零异常的时候,页面会显示自定义异常响应信息,如下图所示:
8. 配置嵌入式Servlet容器
SpringBoot 默认使用 Tomcat 作为嵌入式的 Servlet 容,如下图所示。开发时,我们可以根据自身需要更改 Tomcat 的 Servlet 容器配置。
8.1 定制或配置 Servlet 容器
修改和 server 有关的配置,其中 application.propertise 中可以配置的内容我们可以从 ServerProperties 类中找到。具体配置的实现可以从 EmbeddedServletContainerCustomizer 接口的之类找到。
1
2
3
4
5
6# 通用的Servlet容器设置
# server.xxx
# Tomcat的设置
# server.tomcat.xxx
server.port=8080
server.tomcat.uri-encoding=UTF-8编写一个 EmbeddedServletContainerCustomizer,即 SpringBoot 提供的一个嵌入式的 Servlet 容器定制器,同样也是用来修改 Servlet 容器的配置。具体代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ServletConfiguration {
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
/**
* 定制嵌入式的Servlet容器相关的规则,比如设置端口号
* @param container
*/
public void customize(ConfigurableEmbeddedServletContainer container) {
// 设置访问端口
container.setPort(8081);
// 设置访问路径
container.setContextPath("/shoto");
}
};
}
}测试结果如下所示
8.2 注册 Servlet、Filter、Listener
由于 SpringBoot 默认是以 jar 包的方式启动嵌入式的 Servlet 容器来启动 SpringBoot 的 web 应用,因此没有 web.xml 文件,我们便也就数不能在 web.xml 文件中注册 Servlet、Filter 和 Listener。下面讲一下 SpringBoot 是如何进行这三大组件的注册的。
8.2.1 注册Servlet
- 首先编写一个自定义的 Servlet,部分代码如下所示:
1 | public class MyServlet implements Servlet { |
- 然后在配置类中编写如下代码,这里使用到了FilterRegistrationBean 类,具体代码如下所示:
1 |
|
- 接着在浏览器中访问
http:/localhost:8080/MyServlet
,此时 MyServlet 会开始进行初始化,也就是 MyServlet 中的 init 方法会开始执行。
8.2.2 注册 Filter
首先编写一个 Filter。
然后在配置类中编写如下代码,这里使用到了FilterRegistrationBean 类,具体代码如下所示:
1 |
|
8.2.4 注册 Listener
使用 ServletListenerRegistrationBean 类:
1 |
|
SpringBoot 帮我们自动装载 SpringMVC 的时候,自动的注册 SpringMVC 的前端控制器,即 DIspatcherServlet。
DispatcherServletAutoConfiguration中:
1 | (name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) |
9. 使用外置的Servlet容器
SpringBoot 默认支持的是嵌入式 Servlet 容器,因此应用打成可执行的 Jar。这样的优点是简单和便携。但是缺点就是默认不支持 JSP,而且优化定制也比较复杂。
下面我们来为 SpringBoot 配置外置的 Servlet 容器,以使 SpringBoot 能够兼容 JSP 且支持 SpringBoot 应用以 war 包的方式进行打包。
1.首先是先创建一个war项目,并创建好目录结构,需要注意的关键步骤如下所示:
2.IDEA 中配置 Tomcat 启动容器,配置后的结果如下所示:
3.接着需要确保 POM.xml 文件中进行了如下依赖的配置,具体如下所示:
1 | <dependency> |
4.然后需要注意的是 SpringBootApplication 主程序所在的包下是否生成 ServletInitializer 类,该类是一个核心类,具体内容如下所示:
1 | public class ServletInitializer extends SpringBootServletInitializer { |
5.最后我们可以在 webapp 目录下编写如下两个 jsp 文件,分别是 hello.jsp 和 success.jsp,具体内容如下:
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
6.然后编写一个 HelloController 类,具体内容如下所示:
1 |
|
7.最后一步是在 application.properties 配置文件中进行配置,即配置 jsp 文件的前缀和后缀,具体如下所示:
1 | spring.mvc.view.prefix=/WEB-INF/ |
如此 SpringBoot 便可以兼容 JSP 页面了,SpringBoot 使用外置的 Servlet 容器便完成了。