Spring Boot 对 Servlet 应用的支持与集成 Thymeleaf 示例

186 阅读4分钟

Spring MVC 的自动装配

Spring Boot 适合做 Web 应用开发,简单地引入一个 spring-boot-starter-web 模块就可以快速启动并运行一个 Web 应用。如果想构建一个基于 Servlet 或者 Reactive 的 Web 应用,就可以利用 Spring Boot 的自动装配能力,完成 Spring MVC 的配置。

(1)Spring MVC & WebFlux

Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring Framework 中。Spring Web MVC 的正式名称来自其源代码模块的名称(spring-webmvc),它更常见的名字是 「Spring MVC」。后来在 Spring Framework 5.0 引入了一个响应式 Web 框架,Spring WebFlux 也是来自于源代码模块(spring-webflux)。

spring-mvc-and-webflux

(2)Servlet Web Application

这里我们先介绍下 Spring Boot 对 Servlet Web Application 的支持。

Spring MVC 本身也是一种 MVC web 框架,通过创建 @Controller 或者 @RestController Bean 就可以用来处理 HTTP 请求,借助 @RequestMapping 就可以完成请求到处理方法的映射。

通过 Spring Boot 的自动装配可以节省大量的配置操作,比如不需要声明 @EnableWebMvc,自动注册 ConverterFormatter,支持静态资源导入与 index.html。如果想定制 MVC 的 interceptors, formatters, view controllers 或者其他特性,就实现一个 WebMvcConfigurer 作为 @Configuration 类。如果完全不希望自动装配,也可以直接声明 @EnableWebMvc。

在大量开箱即用的 MVC 特性基础上,Spring Boot 也保持了良好的扩展性。比如用于处理 HTTP 请求与响应的 HttpMessageConverter 接口,用于生成错误信息绑定策略的 MessageCodesResolver,用于处理静态文件的 ResourceHttpRequestHandler,用于处理 Welcome Page 的 WelcomePageHandlerMapping,用于路径映射与内容协商的后缀匹配机制,用于动态处理 HTML 的模板引擎,用于处理跨域的 CorsRegistry

在企业软件应用中大量呈现的前后端分离开发态势,就可以通过 Spring MVC 的模板引擎(Template Engine)来快速集成。Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 JSP。此外,许多其他模板引擎也包含自己的 Spring MVC 集成。比如 Spring MVC 启用 Thymeleaf 实现网页渲染功能

(3)为什么 JSP 会被 Spring 抛弃

曾几何时,Spring MVC 应用大量使用 JSP 生成 HTML 内容。JSP 本身是一项成熟的技术,早在 Java 诞生之初就已存在。但企业开发中的生产效率与 JSP 规范标准有着不可调和的矛盾,JSP 规范完全耦合与 Servlet 标准,而且 JSP 提供了一种类似 HTML 的语法,但是又没法完全兼容 HTML。Thymeleaf 等模板引擎技术应运而生。

从 Spring Boot 生态上来看,不建议使用 JSP 作为模板引擎,尤其是使用嵌入式 Servlet 容器。Tomcat 和 Jetty 只能通过 war 启动,不能通过可执行 jar 启动 JSP,Undertow 干脆不支持 JSP。另外,创建自定义的 error.jsp 页面不会覆盖 Spring MVC 默认的错误处理视图,还需要自定义错误页面(Error Page)。

Spring Boot 集成 Thymeleaf

得益于 Spring Boot 对 Spring MVC 的良好封装,创建 Servlet Web Application 应用与前后端集成都变得容易且透明,接口设计稳定往往不需要关心实现细节。现在看来比起 Spring MVC 有了大幅效率提升。

(1)模板引擎约定

使用 Spring MVC 的话,需要手动配置 Thymeleaf 模板引擎,使用 Spring Boot 的话引入 spring-boot-starter-thymeleaf 就可以完成自动装配了。Spring Boot 默认会从 src/main/resources/templates 路径中读取模板文件。比如将一个 index.html 文件放到这个目录下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home page</title>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<p>
    <span th:text="'Today is: ' + ${#dates.format(#dates.createNow(), 'dd MMM yyyy HH:mm')}" th:remove="tag"></span>
</p>
</body>
</html>

(2)控制器配置

此时,只需要一个非常简单的控制器将网页根路径 / 映射到模板。Spring MVC 中的最佳实践是返回模板文件的字符串名称,减去文件扩展名。因此,要显示上文定义的 index.html 文件,只需要控制器方法需要返回字符串 index 即可。

@Controller 注解使该类成为 Spring 组件和 Spring MVC 控制器。@RequestMapping 注解了 index() 方法,配置了该控制器方法的根路径。

@Controller
public class WebController {

    @RequestMapping("/")
    public String index() {
        return "index";
    }
}

(3)模板引擎定制

以上,就完成了一个最小化的页面渲染功能。如果需要修改模板引擎,可以选择配置这几个重要的 Bean,分别是 ViewResolverSpringTemplateEngineITemplateResolver

当然,下面这些示例其实都是 Spring Boot 自动装配设置的默认值。

@Configuration
public class ViewTemplateConfig {

    @Bean
    public ViewResolver viewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        return resolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        return engine;
    }

    @Bean
    public ClassLoaderTemplateResolver templateResolver() {
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setPrefix("/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCacheable(false);
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    }
}

这里 ITemplateResolver 选择了 ClassLoaderTemplateResolver 作为实现的原因是前端模板就位于类路径上,无需像配置文件一样添加 classpath

ClassLoaderTemplateResolver 作为模板解析器将 Thymeleaf 模板解析成 TemplateResolution 对象,包括模板模式、前后缀、缓存等。与下面的配置效果是一样的:

spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML

同时,需要注意不同版本的 SpringTemplateEngine 和 ThymeleafViewResolver 的包路径是不同的,升降级 Spring 大版本要考虑兼容性。