[Spring实战系列] - No.9 开始SpringMVC之旅(4)

这篇文章,我们将会讲解关于使用Thymeleaf渲染Web网页。这个系列大概还会有一篇文章讲解Spring Security,接来来我就会开始使用Spring-boot+Angular框架来搭建Web应用。

Jsp虽然在表单绑定方面使用很简单,灵活易懂,但是Jsp的网页的格式都不太友好,Jsp文件看上去像是XML和HTML,但是实际上并不是。并且,JSP和Servlet是紧密耦合的,所以我们只能在基于servlet的Web应用中使用JSP。

我们使用之前Spitter项目中的最简单的home.jsp文件,来展示Thymeleaf:

1.配置Thymeleaf:

如果我们想在Spring中使用Thymeleaf,那么我们需要配置以下内容:

a.ThymeleafViewResolver:Thymeleaf视图解释器。

b.SpringTemplateEngine:处理模板渲染结果

c.TemplateResolver:加载Thymeleaf模板

我们依然使用Java来配置Thymeleaf:

package spittr.web;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;

@Configuration
@EnableWebMvc
@ComponentScan("spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter {

  @Bean
  public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setTemplateEngine(templateEngine);
    return viewResolver;
  }
  @Bean
  public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
    return templateEngine;
  }

  @Bean
  public TemplateResolver templateResolver() {
    TemplateResolver templateResolver = new ServletContextTemplateResolver();
    templateResolver.setPrefix("/WEB-INF/views/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("HTML5");
    return templateResolver;
  }
    
  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }
  
}
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输出。


2.Thymeleaf模板:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>Spitter</title>
    <link rel="stylesheet" 
          type="text/css" 
          th:href="@{/resources/style.css}"></link>
  </head>
  <body>
    <div id="header" th:include="page :: header"></div>
  
    <div id="content">
      <h1>Welcome to Spitter</h1>
  
      <a th:href="@{/spittles}">Spittles</a> | 
      <a th:href="@{/spitter/register}">Register</a>
      
      <br/>
      
      View: <span th:text="${view}">unknown</span>
    </div>
    <div id="footer" th:include="page :: copy"></div>
  </body>
</html>
我们在href的前面添加了th:,这样,我们的href标签就可以使用thymeleaf的表达式,计算动态的值,例如本文中的@{/resources/style.css}。JSP最好的功能就是很方便的进行表单的绑定。那么我们的thymeleaf可以很好的实现这个功能么?答案是肯定的。

我们来看一下registration.jsp中的FirstName

<label th:class="${#fields.hasErrors('firstName')}? 'error'">First Name</label>: 
          <input type="text" th:field="*{firstName}"  
                 th:class="${#fields.hasErrors('firstName')}? 'error'" /><br/>
在这里,我们不再使用Spring JSP标签中的cssClassName属性,而是在标准的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

<div id="content">
      <h1>Register</h1>
  
      <form method="POST" th:object="${spitter}">
        <div class="errors" th:if="${#fields.hasErrors('*')}">
          <ul>
            <li th:each="err : ${#fields.errors('*')}" 
                th:text="${err}">Input is incorrect</li>
          </ul>
        </div>
        <label th:class="${#fields.hasErrors('firstName')}? 'error'">First Name</label>: 
          <input type="text" th:field="*{firstName}"  
                 th:class="${#fields.hasErrors('firstName')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('lastName')}? 'error'">Last Name</label>: 
          <input type="text" th:field="*{lastName}"
                 th:class="${#fields.hasErrors('lastName')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('email')}? 'error'">Email</label>: 
          <input type="text" th:field="*{email}"
                 th:class="${#fields.hasErrors('email')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('username')}? 'error'">Username</label>: 
          <input type="text" th:field="*{username}"
                 th:class="${#fields.hasErrors('username')}? 'error'" /><br/>
  
        <label th:class="${#fields.hasErrors('password')}? 'error'">Password</label>: 
          <input type="password" th:field="*{password}"  
                 th:class="${#fields.hasErrors('password')}? 'error'" /><br/>
        <input type="submit" value="Register" />
      
      
      </form>
    </div>
你可以注意到上面程序使用了相同的Thymeleaf属性和“*{}”表达式,为所有的表单域绑定后端对象。这其实重复了我们在First Name域中所做的事情。
但是,需要注意我们在表单的顶部了也使用了Thymeleaf,它会用来渲染所有的错误。<div>元素使用th:if属性来检查是否有校验错误。如果有的话,会渲染<div>,否则的话,它将不会渲染。
在<div>中,会使用一个无顺序的列表来展现每项错误。<li>标签上的th:each属性将会通知Thymeleaf为每项错误都渲染一个<li>,在每次迭代中会将当前错误设置到一个名为err的变量中。
<li>标签还有一个th:text属性。这个命令会通知Thymeleaf计算某一个表达式(在本例中,也就是err变量)并将它的值渲染为<li>标签的内容体。实际上的效果就是每项错误对应一个<li>元素,并展现错误的文本。
你可能会想知道“${}”和“*{}”括起来的表达式到底有什么区别。“${}”表达式(如${spitter})是变量表达式(variable expression)。一般来讲,它们会是对象图导航语言(Object-Graph Navigation Language,OGNL)表达式(http://commons.apache.org/proper/commons-ognl/)。但在使用Spring的时候,它们是SpEL表达式。在${spitter}这个例子中,它会解析为key为spitter的model属性。
而对于“*{}”表达式,它们是选择表达式(selection expression)。变量表达式是基于整个SpEL上下文计算的,而选择表达式是基于某一个选中对象计算的。在本例的表单中,选中对象就是<form>标签中th:object属性所设置的对象:模型中的Spitter对象。因此,“*{firstName}”表达式就会计算为Spitter对象的firstName属性



P.S.文章不妥之处还望指正

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值