编写处理Web请求的控制器。创建视图,用来渲染控制器产生的模型数据。
理解视图解析
如果控制器只通过逻辑视图名来了解视图的话,那Spring该如何确定使用哪一个视图实现来渲染模型呢?这就是Spring视图解析器的任务了。
我们将会看一个没有列在表6.1中的视图解析器。Thymeleaf是一种用来替代JSP的新兴技术,Spring提供了与Thymeleaf的原生模板(natural template)协作的视图解析器,这种模板之所以得到这样的称呼是因为它更像是最终产生的HTML,而不是驱动它们的Java代码。
在这里只提及使用Thymeleaf的部分
Thymeleaf模板是原生的,不依赖于标签库。它能在接受原始HTML的地方进行编辑和渲染。因为它没有与Servlet规范耦合,因此Thymeleaf模板能够进入JSP所无法涉足的领域。现在,我们看一下如何在Spring MVC中使用Thymeleaf。
配置Thymeleaf视图解析器
为了要在Spring中使用Thymeleaf,我们需要配置三个启用Thymeleaf与Spring集成的bean:
ThymeleafViewResolver:将逻辑视图名称解析为Thymeleaf模板视图;
SpringTemplateEngine:处理模板并渲染结果;
TemplateResolver:加载Thymeleaf模板。
如下为声明这些bean的Java配置。
ThymeleafViewResolver是Spring MVC中ViewResolver的一个实现类。像其他的视图解析器一样,它会接受一个逻辑视图名称,并将其解析为视图。不过在该场景下,视图会是一个Thymeleaf模板。
需要注意的是ThymeleafViewResolver bean中注入了一个对SpringTemplate Engine bean的引用。SpringTemplateEngine会在Spring中启用Thymeleaf引擎,用来解析模板,并基于这些模板渲染结果。可以看到,我们为其注入了一个TemplateResolver bean的引用。
TemplateResolver会最终定位和查找模板。与之前配置InternalResource-ViewResolver类似,它使用了prefix和suffix属性。前缀和后缀将会与逻辑视图名组合使用,进而定位Thymeleaf引擎。它的templateMode属性被设置成了HTML 5,这表明我们预期要解 析的模板会渲染成HTML 5输出。
定义Thymeleaf模板
Thymeleaf在很大程度上就是HTML文件,与JSP不同,它没有什么特殊的标签或标签库。Thymeleaf之所以能够发挥作用,是因为它通过自定义的命名空间,为标准的HTML标签集合添加Thymeleaf属性。
th:href属性的特殊之处在于它的值中可以包含Thymeleaf表达式,用来计算动态的值。它会渲染成一个标准的href属性,其中会包含在渲染时动态创建得到的值。这是Thymeleaf命名空间中很多属性的运行方式:它们对应标准的HTML属性,并且具有相同的名称,但是会渲染一些计算后得到的值。在本例中,使用th:href属性的三个地方都用到了“@{}”表达式,用来计算相对于URL的路。径。
借助Thymeleaf实现表单绑定
表单绑定是Spring MVC的一项重要特性。它能够将表单提交的数据填充到命令对象中,并将其传递给控制器,而在展现表单的时候,表单中也会填充命令对象中的值。如果没有表单绑定功能的话,我们需要确保HTML表单域要映射后端命令对象中的属性,并且在校验失败后展现表单的时候,还要负责确保输入域中值要设置为命令对象的属性。 但是,如果有表单绑定的话,它就会负责这些事情了。
请参考如下的Thymeleaf模板片段,它会渲染First Name输入域:
在标准的HTML标签上使用th:class属性。th:class属性会渲染为 一个class属性,它的值是根据给定的表达式计算得到的。在上面的这两个th:class属性中,它会直接检查firstName域有没有校验错误。如果有的话,class属性在渲染时的值为error。如果这个域没有错误的话,将不会渲染class属性。
<input>标签使用了th:field属性,用来引用后端对象的firstName域。这可能与你的预期有点差别。在Thymeleaf模板中,我们在很多情况下所使用的属性都对应于标准的HTML属性,因此貌似使用th:value属性来设置<input>标签的value属性才是合理的。 其实不然,因为我们是在将这个输入域绑定到后端对象的firstName属性上,因此使用th:field属性引用firstName域。通过使用th:field,我们将value属性设置为firstName的值,同时还会将name属性设置为firstName。
程序清单6.7使用了相同的Thymeleaf属性和“*{}”表达式,为所有的表单域绑定后端对象。这其实重复了我们在First Name域中所做的事情。但是,需要注意我们在表单的顶部了也使用了Thymeleaf,它会用来渲染所有的错误。<div>元素使用th:if属性来检查是否有校验错误。如果有的话,会渲染<div>,否则的话,它将不会渲染。
在<div>中,会使用一个无顺序的列表来展现每项错误。<li>标签上的th:each属性将会通知Thymeleaf为每项错误都渲染一个<li>,在每次迭代中会将当前错误设置到一个名为err的变量中。 <li>标签还有一个th:text属性。这个命令会通知Thymeleaf计算某一个表达式(在本例中,也就是err变量)并将它的值渲染为<li>标签的内容体。实际上的效果就是每项错误对应一个<li>元素,并展现错误的文本。
你可能会想知道“${}”和“*{}”括起来的表达式到底有什么区别。“${}”表达式(如${spitter})是变量表达式。一般来讲,它们会是对象图导航语言(Object-Graph Navigation Language,OGNL)表达式。但在使用Spring的时候,它们是SpEL表达式。在${spitter}这个例子中,它会解析为key为spitter的model属性。
而对于“*{}”表达式,它们是选择表达式。变量表达式是基于整个SpEL上下文计算的,而选择表达式是基于某一个选中对象计算的。在本例的表单中,选中对象就是<form>标签中th:object属性所设置的对象:模型中的Spitter对象。因 此,“*{firstName}”表达式就会计算为Spitter对象的firstName属性。
小结
处理请求只是Spring MVC功能的一部分。如果控制器所产生的结果想要让人看到,那么它们产生的模型数据就要渲染到视图中,并展现到用户的Web浏览器中。
我们看到了如何使用Thymeleaf作为Spring MVC应用的视图层,它被视为JSP的替代方案。Thymeleaf是一项很有吸引力的技术,因为它能创建原始的模板,这些模板是纯HTML,能像静态HTML那样以原始的方式编写和预览,并且能够在运行时渲染动态模型数据。除此之外,Thymeleaf是与Servlet没有耦合关系的,这样它就能够用在JSP所不能使用的领域中。