• 作者:老汪软件技巧
  • 发表时间:2024-09-15 10:02
  • 浏览量:

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)。

(2)Servlet Web Application

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

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

通过 Spring Boot 的自动装配可以节省大量的配置操作,比如不需要声明 @EnableWebMvc,自动注册 Converter 和 Formatter,支持静态资源导入与 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 集成。比如 。

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

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

从 Spring Boot 生态上来看,,尤其是使用嵌入式 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 文件放到这个目录下:

html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home pagetitle>
    <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,分别是 ViewResolver、SpringTemplateEngine、ITemplateResolver。

当然,下面这些示例其实都是 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 大版本要考虑兼容性。